Commit 553be0d1b6075c72982114450ec8a8b9d0fb4cc6
1 parent
565aa245
no message
Showing
23 changed files
with
3211 additions
and
49 deletions
CNLivePlayer/Classes/Header/CNLivePlayerDefines.h
| ... | ... | @@ -43,4 +43,287 @@ typedef NS_ENUM(NSInteger, CNLivePlayerDownloaderState) { |
| 43 | 43 | CNLivePlayerDownloaderFinished //完成 |
| 44 | 44 | }; |
| 45 | 45 | |
| 46 | +#pragma mark - 音频播放状态 | |
| 47 | +typedef NS_ENUM(NSUInteger, CNLiveBgmPlayerState) { | |
| 48 | + /// 初始状态 | |
| 49 | + CNLiveBgmPlayerStateInit, | |
| 50 | + /// 背景音正在播放 | |
| 51 | + CNLiveBgmPlayerStateStarting, | |
| 52 | + /// 背景音停止 | |
| 53 | + CNLiveBgmPlayerStateStopped, | |
| 54 | + /// 背景音正在播放 | |
| 55 | + CNLiveBgmPlayerStatePlaying, | |
| 56 | + /// 背景音暂停 | |
| 57 | + CNLiveBgmPlayerStatePaused, | |
| 58 | + /// 背景音播放出错 | |
| 59 | + CNLiveBgmPlayerStateError, | |
| 60 | + /// 背景音被打断 | |
| 61 | + CNLiveBgmPlayerStateInterrupted | |
| 62 | +}; | |
| 63 | + | |
| 64 | +#pragma mark - 美颜 | |
| 65 | +typedef NS_ENUM(NSUInteger, CNLiveBeautyType) { | |
| 66 | + ///无 | |
| 67 | + CNLiveBeautyTypeNone = 0, | |
| 68 | + ///旧美颜 | |
| 69 | + CNLiveBeautyTypeOld, | |
| 70 | + ///红润美颜 | |
| 71 | + CNLiveBeautyTypeRuddy, | |
| 72 | + ///美颜特效 | |
| 73 | + CNLiveBeautyTypeSpecial | |
| 74 | +}; | |
| 75 | + | |
| 76 | +typedef NS_ENUM(NSUInteger, CNLiveSpecialEffects) { | |
| 77 | + ///小清新 | |
| 78 | + CNLiveSpecialEffectsFresh = 0, | |
| 79 | + ///靓丽 | |
| 80 | + CNLiveSpecialEffectsPretty, | |
| 81 | + ///甜美可人 | |
| 82 | + CNLiveSpecialEffectsSweet, | |
| 83 | + ///怀旧 | |
| 84 | + CNLiveSpecialEffectsNostalgia, | |
| 85 | + ///蓝调 | |
| 86 | + CNLiveSpecialEffectsBlues, | |
| 87 | + ///老照片 | |
| 88 | + CNLiveSpecialEffectsOldPicture, | |
| 89 | + ///樱花 | |
| 90 | + CNLiveSpecialEffectsCherry, | |
| 91 | + ///樱花(光线较暗) | |
| 92 | + CNLiveSpecialEffectsCherryDark, | |
| 93 | + ///红润(光线较暗) | |
| 94 | + CNLiveSpecialEffectsRuddyDark, | |
| 95 | + ///阳光(光线较暗) | |
| 96 | + CNLiveSpecialEffectsSunlightDark, | |
| 97 | + ///红润 | |
| 98 | + CNLiveSpecialEffectsRuddy, | |
| 99 | + ///阳光 | |
| 100 | + CNLiveSpecialEffectsSunlight, | |
| 101 | + ///自然 | |
| 102 | + CNLiveSpecialEffectsNature | |
| 103 | +}; | |
| 104 | + | |
| 105 | +#pragma mark - Video Codec ID | |
| 106 | +/*! | |
| 107 | + * @abstract 视频编码器类型 | |
| 108 | + */ | |
| 109 | +typedef NS_ENUM(NSUInteger, CNLiveVideoCodec) { | |
| 110 | + /// 视频编码器 - h264 软件编码器 | |
| 111 | + CNLiveVideoCodec_X264 = 0, | |
| 112 | + /// 视频编码器 - CNLive265 软件编码器 | |
| 113 | + CNLiveVideoCodec_QY265, | |
| 114 | + /// 视频编码器 - iOS VT264硬件编码器 (iOS 8.0以上支持) | |
| 115 | + CNLiveVideoCodec_VT264, | |
| 116 | + /// 视频编码器 - iOS VT265硬件编码器 (iOS 11.0 以上) | |
| 117 | + CNLiveVideoCodec_VT265, | |
| 118 | + /// 视频编码器 - 由SDK自动选择( VT264 > X264) | |
| 119 | + CNLiveVideoCodec_AUTO = 100, | |
| 120 | + /// 视频编码器 - gif | |
| 121 | + CNLiveVideoCodec_GIF, | |
| 122 | +}; | |
| 123 | + | |
| 124 | +/*! | |
| 125 | + * @abstract 音频编码器类型 | |
| 126 | + */ | |
| 127 | +typedef NS_ENUM(NSUInteger, CNLiveAudioCodec) { | |
| 128 | + /// aac音频软件编码器 - AAC_HE | |
| 129 | + CNLiveAudioCodec_AAC_HE = 0, | |
| 130 | + /// aac音频软件编码器 - AAC_LC | |
| 131 | + CNLiveAudioCodec_AAC, | |
| 132 | + /// iOS自带的audiotoolbox音频编码器 - AAC_LC (CPU占用较低,但推荐码率:64kbps单声道,128kbps双声道) | |
| 133 | + CNLiveAudioCodec_AT_AAC, | |
| 134 | + /// aac软件编码器 - AAC_HE_V2 (仅双声道音频可用;如果输入数据为单声道,则自动退化为 AAC_HE) | |
| 135 | + CNLiveAudioCodec_AAC_HE_V2, | |
| 136 | +}; | |
| 137 | + | |
| 138 | +/*! | |
| 139 | + * @abstract 网络自适应模式类型 | |
| 140 | + */ | |
| 141 | +typedef NS_ENUM(NSUInteger, CNLiveBWEstimateMode) { | |
| 142 | + /// 默认模式 (综合模式,比较平稳) | |
| 143 | + CNLiveBWEstMode_Default = 0, | |
| 144 | + /// 流畅优先模式(消极上调, 极速下调) | |
| 145 | + CNLiveBWEstMode_Negtive, | |
| 146 | + | |
| 147 | + /// 禁用网络自适应网络调整 | |
| 148 | + CNLiveBWEstMode_Disable = 1000, | |
| 149 | +}; | |
| 150 | + | |
| 151 | +/// 直播场景 (CNLive内部会根据场景的特征进行参数调优) | |
| 152 | +typedef NS_ENUM(NSUInteger, CNLiveLiveScene) { | |
| 153 | + /// 默认通用场景(不确定场景时使用) | |
| 154 | + CNLiveLiveScene_Default = 0, | |
| 155 | + /// 秀场场景, 主播上半身为主 | |
| 156 | + CNLiveLiveScene_Showself, | |
| 157 | + /// 游戏场景 | |
| 158 | + CNLiveLiveScene_Game, | |
| 159 | + // others comming soon | |
| 160 | +}; | |
| 161 | + | |
| 162 | +/// 录制场景 | |
| 163 | +typedef NS_ENUM(NSUInteger, CNLiveRecScene) { | |
| 164 | + /// 恒定码率场景 | |
| 165 | + CNLiveRecScene_ConstantBitRate = 0, | |
| 166 | + /// 恒定质量场景 | |
| 167 | + CNLiveRecScene_ConstantQuality, | |
| 168 | +}; | |
| 169 | + | |
| 170 | +/// 视频编码性能档次 (视频质量 和 设备资源之间的权衡) | |
| 171 | +typedef NS_ENUM(NSUInteger, CNLiveVideoEncodePerformance) { | |
| 172 | + /// 低功耗: cpu资源消耗低一些,视频质量差一些 | |
| 173 | + CNLiveVideoEncodePer_LowPower= 0, | |
| 174 | + /// 均衡档次: 性价比比较高 | |
| 175 | + CNLiveVideoEncodePer_Balance, | |
| 176 | + /// 高性能: 画面质量高 | |
| 177 | + CNLiveVideoEncodePer_HighPerformance, | |
| 178 | +}; | |
| 179 | + | |
| 180 | +#pragma mark - QYPublisher State | |
| 181 | + | |
| 182 | +/*! | |
| 183 | + * @abstract 采集设备状态 | |
| 184 | + */ | |
| 185 | +typedef NS_ENUM(NSUInteger, CNLiveCaptureState) { | |
| 186 | + /// 设备空闲中 | |
| 187 | + CNLiveCaptureStateIdle, | |
| 188 | + /// 设备工作中 | |
| 189 | + CNLiveCaptureStateCapturing, | |
| 190 | + /// 设备授权被拒绝 | |
| 191 | + CNLiveCaptureStateDevAuthDenied, | |
| 192 | + /// 关闭采集设备中 | |
| 193 | + CNLiveCaptureStateClosingCapture, | |
| 194 | + /// 参数错误,无法打开(比如设置的分辨率,码率当前设备不支持) | |
| 195 | + CNLiveCaptureStateParameterError, | |
| 196 | + /// 设备正忙,请稍后尝试 ( 该状态在发出通知0.5秒后被清除 ) | |
| 197 | + CNLiveCaptureStateDevBusy, | |
| 198 | +}; | |
| 199 | + | |
| 200 | +/*! | |
| 201 | + * @abstract 推流状态 | |
| 202 | + */ | |
| 203 | +typedef NS_ENUM(NSUInteger, CNLiveStreamState) { | |
| 204 | + /// 初始化时状态为空闲 | |
| 205 | + CNLiveStreamStateIdle = 0, | |
| 206 | + /// 连接中 | |
| 207 | + CNLiveStreamStateConnecting, | |
| 208 | + /// 已连接 | |
| 209 | + CNLiveStreamStateConnected, | |
| 210 | + /// 断开连接中 | |
| 211 | + CNLiveStreamStateDisconnecting, | |
| 212 | + /// 推流出错 | |
| 213 | + CNLiveStreamStateError, | |
| 214 | + /// 结束推流成功 | |
| 215 | + CNLiveStopStreamSuccess, | |
| 216 | + /// 结束推流失败 | |
| 217 | + CNLiveStopStreamFailure, | |
| 218 | +}; | |
| 219 | + | |
| 220 | +/*! | |
| 221 | + * @abstract 推流错误码,用于指示推流失败的原因 | |
| 222 | + */ | |
| 223 | +typedef NS_ENUM(NSUInteger, CNLiveStreamErrorCode) { | |
| 224 | + /// 正常无错误 | |
| 225 | + CNLiveStreamErrorCode_NONE = 0, | |
| 226 | + /// (obsolete) | |
| 227 | + CNLiveStreamErrorCode_CNLiveAUTHFAILED __deprecated_enum_msg("auth removed"), | |
| 228 | + /// 当前帧编码失败 | |
| 229 | + CNLiveStreamErrorCode_ENCODE_FRAMES_FAILED, | |
| 230 | + /// 无法打开配置指示的CODEC | |
| 231 | + CNLiveStreamErrorCode_CODEC_OPEN_FAILED, | |
| 232 | + /// 连接出错,检查地址 | |
| 233 | + CNLiveStreamErrorCode_CONNECT_FAILED, | |
| 234 | + /// 网络连接中断 | |
| 235 | + CNLiveStreamErrorCode_CONNECT_BREAK = 5, | |
| 236 | + /// rtmp 推流域名不存在 (CNLive 自定义) | |
| 237 | + CNLiveStreamErrorCode_RTMP_NonExistDomain, | |
| 238 | + /// rtmp 应用名不存在(CNLive 自定义) | |
| 239 | + CNLiveStreamErrorCode_RTMP_NonExistApplication, | |
| 240 | + /// rtmp 流名已存在(CNLive 自定义) | |
| 241 | + CNLiveStreamErrorCode_RTMP_AlreadyExistStreamName, | |
| 242 | + /// rtmp 被黑名单拒绝(CNLive 自定义) | |
| 243 | + CNLiveStreamErrorCode_RTMP_ForbiddenByBlacklist, | |
| 244 | + /// rtmp 内部错误(CNLive 自定义) | |
| 245 | + CNLiveStreamErrorCode_RTMP_InternalError = 10, | |
| 246 | + /// rtmp URL 地址已过期(CNLive 自定义) | |
| 247 | + CNLiveStreamErrorCode_RTMP_URLExpired, | |
| 248 | + /// rtmp URL 地址签名错误(CNLive 自定义) | |
| 249 | + CNLiveStreamErrorCode_RTMP_SignatureDoesNotMatch, | |
| 250 | + /// rtmp URL 中AccessKeyId非法(CNLive 自定义) | |
| 251 | + CNLiveStreamErrorCode_RTMP_InvalidAccessKeyId, | |
| 252 | + /// rtmp URL 中参数错误(CNLive 自定义) | |
| 253 | + CNLiveStreamErrorCode_RTMP_BadParams, | |
| 254 | + /// rtmp URL 中的推流不在发布点内(CNLive 自定义) | |
| 255 | + CNLiveStreamErrorCode_RTMP_ForbiddenByRegion = 15, | |
| 256 | + /// (obsolete) | |
| 257 | + CNLiveStreamErrorCode_FRAMES_THRESHOLD, | |
| 258 | + /// (obsolete) | |
| 259 | + CNLiveStreamErrorCode_NO_INPUT_SAMPLE, | |
| 260 | + /// 对于URL中的域名解析失败 | |
| 261 | + CNLiveStreamErrorCode_DNS_Parse_failed, | |
| 262 | + /// 对于URL对应的服务器连接失败(无法建立TCP连接) | |
| 263 | + CNLiveStreamErrorCode_Connect_Server_failed, | |
| 264 | + /// 跟RTMP服务器完成握手后,向{appname}/{streamname} 推流失败 | |
| 265 | + CNLiveStreamErrorCode_RTMP_Publish_failed = 20, | |
| 266 | + /// 音视频同步失败 (输入的音频和视频的时间戳的差值超过5s) | |
| 267 | + CNLiveStreamErrorCode_AV_SYNC_ERROR, | |
| 268 | + /// 非法地址(地址为空或url中的协议或本地文件的后缀SDK不支持, 请检查) | |
| 269 | + CNLiveStreamErrorCode_INVALID_ADDRESS, | |
| 270 | + /// 网络不通 | |
| 271 | + CNLiveStreamErrorCode_NETWORK_UNREACHABLE, | |
| 272 | + /// 获取user id失败 (CNLive 自定义) | |
| 273 | + CNLiveStreamErrorCode_RTMP_GetUserIdFailed, | |
| 274 | + /// AK和user id不匹配 (CNLive 自定义) | |
| 275 | + CNLiveStreamErrorCode_RTMP_AkAndUserIsNotMatch = 25, | |
| 276 | + /// 获取服务器信息失败 (CNLive 自定义) | |
| 277 | + CNLiveStreamErrorCode_RTMP_GetServerInfoFailed, | |
| 278 | + /// 非法的外部url (CNLive 自定义) | |
| 279 | + CNLiveStreamErrorCode_RTMP_IllegalOutsideUrl, | |
| 280 | + /// 外部鉴权失败 (CNLive 自定义) | |
| 281 | + CNLiveStreamErrorCode_RTMP_OutsideAuthFailed, | |
| 282 | + /// 简单鉴权失败(CNLive 自定义) | |
| 283 | + CNLiveStreamErrorCode_RTMP_SimpleAuthFailed, | |
| 284 | + /// 无效的鉴权类型 (CNLive 自定义) | |
| 285 | + CNLiveStreamErrorCode_RTMP_InvalidAuthType = 30, | |
| 286 | + /// 非法的user id (CNLive 自定义) | |
| 287 | + CNLiveStreamErrorCode_RTMP_IllegalUserId, | |
| 288 | + /// 异常 | |
| 289 | + CNLiveStreamErrorCode_ABNORMAL, | |
| 290 | + | |
| 291 | +}; | |
| 292 | + | |
| 293 | +typedef NS_ENUM(NSUInteger, CNLiveReconnectStatus) { | |
| 294 | + CNLiveReconnectStatusStart = 0, | |
| 295 | + CNLiveReconnectStatusSuccess, | |
| 296 | + CNLiveReconnectStatusFailed | |
| 297 | +}; | |
| 298 | + | |
| 299 | +#pragma mark - 旁路录制 | |
| 300 | +/* | |
| 301 | + * @abstract 旁路录制状态 | |
| 302 | + */ | |
| 303 | +typedef NS_ENUM(NSInteger, CNLiveRecordState) { | |
| 304 | + /// 初始状态 | |
| 305 | + CNLiveRecordStateIdle, | |
| 306 | + /// 录像中 | |
| 307 | + CNLiveRecordStateRecording, | |
| 308 | + /// 录像停止 | |
| 309 | + CNLiveRecordStateStopped, | |
| 310 | + /// 录像失败 | |
| 311 | + CNLiveRecordStateError, | |
| 312 | +}; | |
| 313 | + | |
| 314 | +/*! | |
| 315 | + * @abstract 旁路录制错误码 | |
| 316 | + */ | |
| 317 | +typedef NS_ENUM(NSInteger, CNLiveRecordError) { | |
| 318 | + /// 无错误 | |
| 319 | + CNLiveRecordErrorNone, | |
| 320 | + /// 地址错误 | |
| 321 | + CNLiveRecordErrorPathInvalid, | |
| 322 | + /// 格式不支持 | |
| 323 | + CNLiveRecordErrorFormatNotSupport, | |
| 324 | + /// 内部错误 | |
| 325 | + CNLiveRecordErrorInternal, | |
| 326 | +}; | |
| 327 | + | |
| 328 | + | |
| 46 | 329 | #endif /* CNLivePlayerDefines_h */ | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveAudioCapture.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveAudioCapture.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | + | |
| 11 | +NS_ASSUME_NONNULL_BEGIN | |
| 12 | +/** | |
| 13 | + * @abstract 噪声抑制等级 | |
| 14 | + */ | |
| 15 | +typedef NS_ENUM(NSInteger, CNLiveAudioNoiseSuppress){ | |
| 16 | + /// 关闭 | |
| 17 | + CNLiveAudioNoiseSuppress_OFF = -1, | |
| 18 | + /// 温和 | |
| 19 | + CNLiveAudioNoiseSuppress_LOW = 0, | |
| 20 | + /// 中等 | |
| 21 | + CNLiveAudioNoiseSuppress_MEDIUM = 1, | |
| 22 | + /// 激进 | |
| 23 | + CNLiveAudioNoiseSuppress_HIGH = 2, | |
| 24 | + /// 非常激进 | |
| 25 | + CNLiveAudioNoiseSuppress_VERYHIGH = 3, | |
| 26 | +}; | |
| 27 | + | |
| 28 | +@interface CNLiveAudioCapture : NSObject | |
| 29 | + | |
| 30 | +/** | |
| 31 | + @abstract 是否有耳机麦克风可用 | |
| 32 | + @return 是/否有耳机麦克风 | |
| 33 | + */ | |
| 34 | ++ (BOOL)isHeadsetPluggedIn; | |
| 35 | + | |
| 36 | +/** | |
| 37 | + @abstract 是否播放采集的声音 (又称"耳返") | |
| 38 | + @warning 如果在没有插入耳机的情况下启动, 容易出现很刺耳的声音 | |
| 39 | + */ | |
| 40 | +@property(nonatomic, assign) BOOL bPlayCapturedAudio; | |
| 41 | + | |
| 42 | +/** | |
| 43 | + @abstract 设置mic采集的声音音量 | |
| 44 | + @discussion 调整范围 0.0~1.0 | |
| 45 | + */ | |
| 46 | +@property(nonatomic, assign) float micVolume; | |
| 47 | + | |
| 48 | +/** | |
| 49 | + @abstract 设置耳返音量 | |
| 50 | + @discussion 调整范围 0.0~1.0 | |
| 51 | + */ | |
| 52 | +@property(nonatomic, assign) Float32 bPlayVolume; | |
| 53 | + | |
| 54 | +/** | |
| 55 | + @abstract 混响类型 | |
| 56 | + @discussion 目前提供了4种类型的混响场景, type和场景的对应关系如下 | |
| 57 | + | |
| 58 | + - 0 关闭 | |
| 59 | + - 1 录音棚 | |
| 60 | + - 2 KTV | |
| 61 | + - 3 小舞台 | |
| 62 | + - 4 演唱会 | |
| 63 | + */ | |
| 64 | +@property(nonatomic, assign) int reverbType; | |
| 65 | + | |
| 66 | +/** | |
| 67 | + @abstract 音效类型 | |
| 68 | + @discussion 目前提供了4种音效类型, type和音效的对应关系如下 | |
| 69 | + | |
| 70 | + - 0 关闭 | |
| 71 | + - 1 大叔 | |
| 72 | + - 2 萝莉 | |
| 73 | + - 3 庄严 | |
| 74 | + - 4 机器人 | |
| 75 | + */ | |
| 76 | +@property (nonatomic, assign) int audioEffectType; | |
| 77 | + | |
| 78 | +/** | |
| 79 | + @abstract 噪声抑制处理的等级(默认为CNLiveAudioNoiseSuppress_OFF) | |
| 80 | + @discussion 当启用噪声抑制处理后, 内部会进行输出音频数据的 | |
| 81 | + */ | |
| 82 | +@property (nonatomic, assign) CNLiveAudioNoiseSuppress noiseSuppressionLevel; | |
| 83 | + | |
| 84 | +@end | |
| 85 | + | |
| 86 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveAudioCapture.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveAudioCapture.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveAudioCapture.h" | |
| 10 | +#import "CNLiveStreamerManager.h" | |
| 11 | + | |
| 12 | +#define STREAMER_Manager_AudioCap [CNLiveStreamerManager manager].kit.aCapDev | |
| 13 | + | |
| 14 | +@implementation CNLiveAudioCapture | |
| 15 | +//是否有麦克风可用 | |
| 16 | ++ (BOOL)isHeadsetPluggedIn { | |
| 17 | + return [KSYAUAudioCapture isHeadsetPluggedIn]; | |
| 18 | +} | |
| 19 | + | |
| 20 | +//设置耳返 | |
| 21 | +- (void)setBPlayCapturedAudio:(BOOL)bPlayCapturedAudio { | |
| 22 | + _bPlayCapturedAudio = bPlayCapturedAudio; | |
| 23 | + STREAMER_Manager_AudioCap.bPlayCapturedAudio = bPlayCapturedAudio; | |
| 24 | +} | |
| 25 | + | |
| 26 | +//设置mic声音音量 | |
| 27 | +- (void)setMicVolume:(float)micVolume { | |
| 28 | + _micVolume = micVolume; | |
| 29 | + [STREAMER_Manager.aMixer setMixVolume:micVolume of:STREAMER_Manager.micTrack]; | |
| 30 | +} | |
| 31 | + | |
| 32 | +//耳返音量 | |
| 33 | +- (void)setBPlayVolume:(Float32)bPlayVolume { | |
| 34 | + _bPlayVolume = bPlayVolume; | |
| 35 | + STREAMER_Manager_AudioCap.micVolume = bPlayVolume; | |
| 36 | +} | |
| 37 | + | |
| 38 | +//混响类型 | |
| 39 | +- (void)setReverbType:(int)reverbType { | |
| 40 | + _reverbType = reverbType; | |
| 41 | + STREAMER_Manager_AudioCap.reverbType = reverbType; | |
| 42 | +} | |
| 43 | + | |
| 44 | +//音效类型 | |
| 45 | +- (void)setAudioEffectType:(int)audioEffectType { | |
| 46 | + _audioEffectType = audioEffectType; | |
| 47 | + STREAMER_Manager_AudioCap.effectType = (KSYAudioEffectType)audioEffectType; | |
| 48 | +} | |
| 49 | + | |
| 50 | +//降噪 | |
| 51 | +- (void)setNoiseSuppressionLevel:(CNLiveAudioNoiseSuppress)noiseSuppressionLevel { | |
| 52 | + _noiseSuppressionLevel = noiseSuppressionLevel; | |
| 53 | + STREAMER_Manager_AudioCap.noiseSuppressionLevel = (KSYAudioNoiseSuppress)noiseSuppressionLevel; | |
| 54 | +} | |
| 55 | + | |
| 56 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveBeautifyFaceFilter.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveBeautifyFaceFilter.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import "CNLivePlayerDefines.h" | |
| 11 | + | |
| 12 | +NS_ASSUME_NONNULL_BEGIN | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 美颜((默认旧美颜,磨皮参数为0.5)) | |
| 16 | + */ | |
| 17 | +@interface CNLiveBeautifyFaceFilter : NSObject | |
| 18 | + | |
| 19 | +/** | |
| 20 | + @abstract CNLiveBeautyTypeSpecial 美颜特效类型,需先设置CNLiveBeautyType为CNLiveBeautyTypeSpecial | |
| 21 | + */ | |
| 22 | +- (void)setCurEffectIdx:(CNLiveSpecialEffects)curEffectIdx; //美颜特效类型 | |
| 23 | + | |
| 24 | +/** | |
| 25 | + @abstract 美颜类型 | |
| 26 | + @param beautyType 类型 | |
| 27 | + @warn 旧美颜仅grindRatio参数可调节,另两个参数设置无效 | |
| 28 | + */ | |
| 29 | +- (void)setBeautyType:(CNLiveBeautyType)beautyType; | |
| 30 | + | |
| 31 | +/** | |
| 32 | + @abstract 美颜参数调节 | |
| 33 | + @param grindRatio 磨皮 | |
| 34 | + @param whitenRatio 美白 | |
| 35 | + @param intensity 特效 | |
| 36 | + */ | |
| 37 | +- (void)filterAdjustingParameterWithGrindRatio:(CGFloat)grindRatio whitenRatio:(CGFloat)whitenRatio intensity:(CGFloat)intensity; | |
| 38 | + | |
| 39 | +@end | |
| 40 | + | |
| 41 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveBeautifyFaceFilter.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveBeautifyFaceFilter.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveBeautifyFaceFilter.h" | |
| 10 | +#import <libksygpulive/KSYGPUStreamerKit.h> | |
| 11 | +#import "CNLiveStreamerManager.h" | |
| 12 | + | |
| 13 | +@interface CNLiveBeautifyFaceFilter () { | |
| 14 | + CNLiveBeautyType _currentType; | |
| 15 | + CNLiveSpecialEffects _curEffectIdx; | |
| 16 | + | |
| 17 | +} | |
| 18 | + | |
| 19 | +@property (nonatomic, readonly) GPUImageOutput<GPUImageInput>* curFilter; | |
| 20 | + | |
| 21 | +@property (nonatomic, assign) CGFloat grindRatio; //磨皮 | |
| 22 | + | |
| 23 | +@property (nonatomic, assign) CGFloat whitenRatio; //美白 | |
| 24 | + | |
| 25 | +@property (nonatomic, assign) CGFloat intensity; //特效 | |
| 26 | + | |
| 27 | +@end | |
| 28 | + | |
| 29 | +@implementation CNLiveBeautifyFaceFilter | |
| 30 | + | |
| 31 | +- (instancetype)init { | |
| 32 | + self = [super init]; | |
| 33 | + if (self) { | |
| 34 | + | |
| 35 | + _grindRatio = 0.5; //初始化参数 | |
| 36 | + _whitenRatio = 0.5; | |
| 37 | + _intensity = 0.5; | |
| 38 | + | |
| 39 | + _curFilter = [[KSYGPUBeautifyExtFilter alloc] init]; | |
| 40 | + int val = (_grindRatio*5) + 1; // level 1~5 | |
| 41 | + [(KSYGPUBeautifyExtFilter *)_curFilter setBeautylevel: val]; | |
| 42 | + | |
| 43 | + } | |
| 44 | + return self; | |
| 45 | +} | |
| 46 | + | |
| 47 | +- (void)setBeautyType:(CNLiveBeautyType)beautyType { | |
| 48 | + _currentType = beautyType; | |
| 49 | + | |
| 50 | + switch (beautyType) { | |
| 51 | + case 0: //无美颜 | |
| 52 | + _curFilter = [[KSYGPUBeautifyExtFilter alloc] init]; | |
| 53 | + [(KSYGPUBeautifyExtFilter *)_curFilter setBeautylevel: 1]; | |
| 54 | + break; | |
| 55 | + | |
| 56 | + case 1: //旧美颜 | |
| 57 | + _curFilter = [[KSYGPUBeautifyExtFilter alloc] init]; | |
| 58 | + int val = (_grindRatio*5) + 1; // level 1~5 | |
| 59 | + [(KSYGPUBeautifyExtFilter *)_curFilter setBeautylevel: val]; | |
| 60 | + break; | |
| 61 | + | |
| 62 | + case 2: //红润美颜 | |
| 63 | + { | |
| 64 | + UIImage *rubbyMat = [self cnliveGPUImageNamed:@"3_tianmeikeren.png"]; | |
| 65 | + KSYBeautifyFaceFilter * bf = [[KSYBeautifyFaceFilter alloc] initWithRubbyMaterial:rubbyMat]; | |
| 66 | + bf.grindRatio = _grindRatio; | |
| 67 | + bf.whitenRatio = _whitenRatio; | |
| 68 | + bf.ruddyRatio = _intensity; | |
| 69 | + _curFilter = bf; | |
| 70 | + } | |
| 71 | + | |
| 72 | + break; | |
| 73 | + | |
| 74 | + case 3: //特效美颜 | |
| 75 | + { | |
| 76 | + // 构造美颜滤镜 和 特效滤镜 | |
| 77 | + KSYBeautifyFaceFilter * bf = [[KSYBeautifyFaceFilter alloc] init]; | |
| 78 | + KSYBuildInSpecialEffects * sf = [[KSYBuildInSpecialEffects alloc] initWithIdx:_curEffectIdx+1]; | |
| 79 | + bf.grindRatio = _grindRatio; | |
| 80 | + bf.whitenRatio = _whitenRatio; | |
| 81 | + sf.intensity = _intensity; | |
| 82 | + [bf addTarget:sf]; | |
| 83 | + | |
| 84 | + // 用滤镜组 将 滤镜 串联成整体 | |
| 85 | + GPUImageFilterGroup * fg = [[GPUImageFilterGroup alloc] init]; | |
| 86 | + [fg addFilter:bf]; | |
| 87 | + [fg addFilter:sf]; | |
| 88 | + | |
| 89 | + [fg setInitialFilters:[NSArray arrayWithObject:bf]]; | |
| 90 | + [fg setTerminalFilter:sf]; | |
| 91 | + _curFilter = fg; | |
| 92 | + | |
| 93 | + } | |
| 94 | + break; | |
| 95 | + | |
| 96 | + default: | |
| 97 | + break; | |
| 98 | + } | |
| 99 | + | |
| 100 | + [STREAMER_Manager setupFilter:_curFilter]; | |
| 101 | +} | |
| 102 | + | |
| 103 | +//设置特效美颜类型 | |
| 104 | +- (void)setCurEffectIdx:(CNLiveSpecialEffects)curEffectIdx { | |
| 105 | + if (_currentType != 3) return; | |
| 106 | + | |
| 107 | + _curEffectIdx = curEffectIdx; | |
| 108 | + | |
| 109 | + if ( [_curFilter isMemberOfClass:[GPUImageFilterGroup class]]){ | |
| 110 | + GPUImageFilterGroup * fg = (GPUImageFilterGroup *)_curFilter; | |
| 111 | + KSYBuildInSpecialEffects * sf = (KSYBuildInSpecialEffects *)[fg filterAtIndex:1]; | |
| 112 | + [sf setSpecialEffectsIdx: curEffectIdx+1]; | |
| 113 | + } | |
| 114 | + | |
| 115 | +} | |
| 116 | + | |
| 117 | +//设置美颜参数 | |
| 118 | +- (void)filterAdjustingParameterWithGrindRatio:(CGFloat)grindRatio whitenRatio:(CGFloat)whitenRatio intensity:(CGFloat)intensity { | |
| 119 | + _grindRatio = grindRatio; | |
| 120 | + _whitenRatio = whitenRatio; | |
| 121 | + _intensity = intensity; | |
| 122 | + | |
| 123 | + if (_currentType == 1) { //旧美颜 | |
| 124 | + | |
| 125 | + int val = (grindRatio*5) + 1; // level 1~5 | |
| 126 | + [(KSYGPUBeautifyExtFilter *)_curFilter setBeautylevel: val]; | |
| 127 | + | |
| 128 | + } else if (_currentType == 2) { //红润美颜 | |
| 129 | + | |
| 130 | + KSYBeautifyProFilter * f =(KSYBeautifyProFilter*)_curFilter; | |
| 131 | + f.grindRatio = grindRatio; | |
| 132 | + f.whitenRatio = whitenRatio; | |
| 133 | + f.ruddyRatio = intensity; | |
| 134 | + | |
| 135 | + } else if (_currentType == 3) { //特效美颜 | |
| 136 | + | |
| 137 | + GPUImageFilterGroup * fg = (GPUImageFilterGroup *)_curFilter; | |
| 138 | + KSYBeautifyFaceFilter * bf = (KSYBeautifyFaceFilter *)[fg filterAtIndex:0]; | |
| 139 | + KSYBuildInSpecialEffects * sf = (KSYBuildInSpecialEffects *)[fg filterAtIndex:1]; | |
| 140 | + bf.grindRatio = grindRatio; | |
| 141 | + bf.whitenRatio = whitenRatio; | |
| 142 | + [sf setIntensity:intensity]; | |
| 143 | + | |
| 144 | + } | |
| 145 | +} | |
| 146 | + | |
| 147 | +#pragma mark - load resource from resource bundle | |
| 148 | +//获取美颜图片资源 | |
| 149 | +- (NSBundle*)cnliveGPUResourceBundle { | |
| 150 | + static dispatch_once_t onceToken; | |
| 151 | + static NSBundle *resBundle = nil; | |
| 152 | + dispatch_once(&onceToken, ^{ | |
| 153 | + resBundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"KSYGPUResource" withExtension:@"bundle"]]; | |
| 154 | + if (resBundle == nil) { | |
| 155 | + resBundle = [self getBundleWithName:@"KSYGPUResource"]; | |
| 156 | + } | |
| 157 | + }); | |
| 158 | + return resBundle; | |
| 159 | +} | |
| 160 | + | |
| 161 | +- (NSBundle *)getBundleWithName:(NSString *)bundleName{ | |
| 162 | + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; | |
| 163 | + NSURL *url = [bundle URLForResource:bundleName withExtension:@"bundle"]; | |
| 164 | + NSBundle *targetBundle = [NSBundle bundleWithURL:url]; | |
| 165 | + return targetBundle; | |
| 166 | +} | |
| 167 | + | |
| 168 | +- (UIImage*)cnliveGPUImageNamed:(NSString*)name { | |
| 169 | + UIImage *imageFromMainBundle = [UIImage imageNamed:name]; | |
| 170 | + if (imageFromMainBundle) { | |
| 171 | + return imageFromMainBundle; | |
| 172 | + } | |
| 173 | + UIImage *imageFromKSYBundle = [UIImage imageWithContentsOfFile:[[[self cnliveGPUResourceBundle] resourcePath] stringByAppendingPathComponent:name]]; | |
| 174 | + return imageFromKSYBundle; | |
| 175 | +} | |
| 176 | + | |
| 177 | +@end | |
| 178 | + | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveBgmPlayer.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveBgmPlayer.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <AVFoundation/AVFoundation.h> | |
| 11 | +#import "CNLivePlayerDefines.h" | |
| 12 | + | |
| 13 | +NS_ASSUME_NONNULL_BEGIN | |
| 14 | + | |
| 15 | +@interface CNLiveBgmPlayer : NSObject | |
| 16 | + | |
| 17 | +// Posted when audio state changes | |
| 18 | +FOUNDATION_EXPORT NSString *const CNLiveBgmAudioStateDidChangeNotification; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + @abstract 开始播放背景音乐 | |
| 22 | + @param path 本地音乐的路径 | |
| 23 | + @param loop 是否循环播放此音乐 | |
| 24 | + @return 是否能够开始播放 | |
| 25 | + */ | |
| 26 | +- (BOOL)startPlayBgm:(NSString *)path isLoop:(BOOL)loop; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + @abstract 停止播放背景音乐 | |
| 30 | + */ | |
| 31 | +- (void)stopPlayBgm; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + @abstract 暂停播放背景音乐 | |
| 35 | + */ | |
| 36 | +- (void)pauseBgm; | |
| 37 | + | |
| 38 | +/** | |
| 39 | + @abstract 恢复播放背景音乐 | |
| 40 | + */ | |
| 41 | +- (void)resumeBgm; | |
| 42 | + | |
| 43 | +/** | |
| 44 | + @abstract seek到指定时间 (拖动进度条) | |
| 45 | + @param time 时间, 请参考 bgmDuration (单位,秒) | |
| 46 | + @return 是否seek 成功 | |
| 47 | + */ | |
| 48 | +- (BOOL)seekToTime:(float)time; | |
| 49 | + | |
| 50 | +/** | |
| 51 | + @abstract seek到指定进度 (拖动进度条) | |
| 52 | + @param prog 进度, 请参考 bgmProgress | |
| 53 | + @return 是否seek 成功 | |
| 54 | + */ | |
| 55 | +- (BOOL)seekToProgress:(float)prog; | |
| 56 | + | |
| 57 | +/** | |
| 58 | + @abstract 背景音乐的音量 | |
| 59 | + @discussion 调整范围 0.0~1.0 | |
| 60 | + @discussion 仅仅调整播放的音量, 不影响回调的音频数据 | |
| 61 | + */ | |
| 62 | +@property (nonatomic, assign) double bgmVolume; | |
| 63 | + | |
| 64 | +/** | |
| 65 | + @abstract 播放声音的音调 | |
| 66 | + @discussion 调整范围 [-24.0 ~ 24.0], 默认为0.01, 单位为半音 | |
| 67 | + @discussion 0.01 为1度, 1.0为一个半音, 12个半音为1个八度 | |
| 68 | + */ | |
| 69 | +@property (nonatomic, assign) double bgmPitch; | |
| 70 | + | |
| 71 | +/** | |
| 72 | + @abstract 背景音乐播放静音 | |
| 73 | + @discussion 仅仅静音播放, 不影响回调的音频数据 | |
| 74 | + */ | |
| 75 | +@property (nonatomic, assign) BOOL bMutBgmPlay; | |
| 76 | + | |
| 77 | +#pragma mark - callbacks | |
| 78 | +/** | |
| 79 | + @abstract 音频数据输出回调 | |
| 80 | + @discussion sampleBuffer 从音乐文件中解码得到的PCM数据 | |
| 81 | + */ | |
| 82 | +@property (nonatomic, copy) BOOL (^cnBgmAudioDataBlock)(uint8_t** pData, int len, const AudioStreamBasicDescription* fmt, CMTime pts); | |
| 83 | + | |
| 84 | +/** | |
| 85 | + @abstract 当背景音乐播放完成时,调用此回调函数 | |
| 86 | + @discussion 只有设置 loop为NO时才有效, 在开始播放前设置有效 | |
| 87 | + */ | |
| 88 | +@property (nonatomic, copy) void(^cnBgmFinishBlock)(void); | |
| 89 | + | |
| 90 | +#pragma mark - player state | |
| 91 | +/** | |
| 92 | + @abstract 背景音的duration信息(总时长, 单位:秒) | |
| 93 | + */ | |
| 94 | +@property (nonatomic, readonly) float bgmDuration; | |
| 95 | + | |
| 96 | +/** | |
| 97 | + @abstract 背景音的已经播放长度 (单位:秒) | |
| 98 | + @discussion 从0开始,最大为bgmDuration长度 | |
| 99 | + */ | |
| 100 | +@property (nonatomic, readonly) float bgmPlayTime; | |
| 101 | + | |
| 102 | +/** | |
| 103 | + @abstract 音频的播放进度 | |
| 104 | + @discussion 取值从0.0~1.0,大小为bgmPlayTime/bgmDuration; | |
| 105 | + */ | |
| 106 | +@property (nonatomic, readonly) float bgmProcess; | |
| 107 | + | |
| 108 | +/** | |
| 109 | + @abstract 音频播放是否运行 | |
| 110 | + @discussion 音频是否输出到speaker播放 | |
| 111 | + */ | |
| 112 | +@property (nonatomic, readonly) BOOL isRunning; | |
| 113 | + | |
| 114 | +/** | |
| 115 | + @abstract 播放错误码 | |
| 116 | + @discussion 播放错误码具体内容可以参考AudioQueue的Apple文档。 | |
| 117 | + */ | |
| 118 | +@property (nonatomic, readonly) OSStatus audioErrorCode; | |
| 119 | + | |
| 120 | +/** | |
| 121 | + @abstract 播放状态 | |
| 122 | + */ | |
| 123 | +@property (nonatomic, readonly) CNLiveBgmPlayerState bgmPlayerState; | |
| 124 | + | |
| 125 | +/** | |
| 126 | + @abstract 单曲循环 | |
| 127 | + */ | |
| 128 | +@property (nonatomic, assign) BOOL bLoop; | |
| 129 | + | |
| 130 | + | |
| 131 | +@end | |
| 132 | + | |
| 133 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveBgmPlayer.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveBgmPlayer.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveBgmPlayer.h" | |
| 10 | +#import "CNLiveStreamerManager.h" | |
| 11 | + | |
| 12 | +#define STREAMER_Manager_BgmPlayer [CNLiveStreamerManager manager].kit.bgmPlayer | |
| 13 | + | |
| 14 | +NSString * const CNLiveBgmAudioStateDidChangeNotification = @"CNLiveBgmAudioStateDidChangeNotification"; | |
| 15 | + | |
| 16 | +@implementation CNLiveBgmPlayer | |
| 17 | + | |
| 18 | +- (instancetype)init { | |
| 19 | + self = [super init]; | |
| 20 | + if (self) { | |
| 21 | + [self addBgmObservers]; | |
| 22 | + | |
| 23 | + STREAMER_Manager_BgmPlayer.audioDataBlock = ^ BOOL(uint8_t** pData, int len, const AudioStreamBasicDescription* fmt, CMTime pts){ | |
| 24 | + [STREAMER_Manager.aMixer processAudioData:pData nbSample:len withFormat:fmt timeinfo:pts of:STREAMER_Manager.bgmTrack]; | |
| 25 | + return YES; | |
| 26 | + }; | |
| 27 | + | |
| 28 | + STREAMER_Manager_BgmPlayer.bgmFinishBlock = ^ { | |
| 29 | + if (self.cnBgmFinishBlock) { | |
| 30 | + self.cnBgmFinishBlock(); | |
| 31 | + } | |
| 32 | + }; | |
| 33 | + | |
| 34 | + } | |
| 35 | + return self; | |
| 36 | +} | |
| 37 | + | |
| 38 | +- (void)addBgmObservers { | |
| 39 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 40 | + selector:@selector(audioStateDidChange) | |
| 41 | + name:KSYAudioStateDidChangeNotification | |
| 42 | + object:nil]; | |
| 43 | +} | |
| 44 | + | |
| 45 | +- (CNLiveBgmPlayerState)audioStateDidChange { | |
| 46 | + return (CNLiveBgmPlayerState)STREAMER_Manager_BgmPlayer.bgmPlayerState; | |
| 47 | +} | |
| 48 | + | |
| 49 | +- (void)dealloc { | |
| 50 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 51 | + name:KSYAudioStateDidChangeNotification | |
| 52 | + object:nil]; | |
| 53 | +} | |
| 54 | + | |
| 55 | +- (BOOL)startPlayBgm:(NSString *)path isLoop:(BOOL)loop { | |
| 56 | + return [STREAMER_Manager_BgmPlayer startPlayBgm:path isLoop:loop]; | |
| 57 | +} | |
| 58 | + | |
| 59 | +- (void)stopPlayBgm { | |
| 60 | + [STREAMER_Manager_BgmPlayer stopPlayBgm]; | |
| 61 | +} | |
| 62 | + | |
| 63 | +- (void)pauseBgm { | |
| 64 | + [STREAMER_Manager_BgmPlayer pauseBgm]; | |
| 65 | +} | |
| 66 | + | |
| 67 | +- (void)resumeBgm { | |
| 68 | + [STREAMER_Manager_BgmPlayer resumeBgm]; | |
| 69 | +} | |
| 70 | + | |
| 71 | +#pragma mark - setter | |
| 72 | +- (void)setBgmVolume:(double)bgmVolume { | |
| 73 | + STREAMER_Manager_BgmPlayer.bgmVolume = bgmVolume; | |
| 74 | +} | |
| 75 | + | |
| 76 | +- (void)setBgmPitch:(double)bgmPitch { | |
| 77 | + STREAMER_Manager_BgmPlayer.bgmPitch = bgmPitch; | |
| 78 | +} | |
| 79 | + | |
| 80 | +- (void)setBMutBgmPlay:(BOOL)bMutBgmPlay { | |
| 81 | + STREAMER_Manager_BgmPlayer.bMuteBgmPlay = bMutBgmPlay; | |
| 82 | +} | |
| 83 | + | |
| 84 | +- (void)setBLoop:(BOOL)bLoop { | |
| 85 | + STREAMER_Manager_BgmPlayer.bLoop = bLoop; | |
| 86 | +} | |
| 87 | + | |
| 88 | +#pragma mark - getter | |
| 89 | +- (BOOL)seekToTime:(float)time { | |
| 90 | + return [STREAMER_Manager_BgmPlayer seekToTime:time]; | |
| 91 | +} | |
| 92 | + | |
| 93 | +- (BOOL)seekToProgress:(float)prog { | |
| 94 | + return [STREAMER_Manager_BgmPlayer seekToProgress:prog]; | |
| 95 | +} | |
| 96 | + | |
| 97 | +- (float)bgmDuration { | |
| 98 | + return STREAMER_Manager_BgmPlayer.bgmDuration; | |
| 99 | +} | |
| 100 | + | |
| 101 | +- (float)bgmPlayTime { | |
| 102 | + return STREAMER_Manager_BgmPlayer.bgmPlayTime; | |
| 103 | +} | |
| 104 | + | |
| 105 | +- (float)bgmProcess { | |
| 106 | + return STREAMER_Manager_BgmPlayer.bgmProcess; | |
| 107 | +} | |
| 108 | + | |
| 109 | +- (BOOL)isRunning { | |
| 110 | + return STREAMER_Manager_BgmPlayer.isRunning; | |
| 111 | +} | |
| 112 | + | |
| 113 | +- (OSStatus)audioErrorCode { | |
| 114 | + return STREAMER_Manager_BgmPlayer.audioErrorCode; | |
| 115 | +} | |
| 116 | + | |
| 117 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamer.m deleted
100644 → 0
CNLivePlayer/Classes/Streamer/CNLiveStreamerBase.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerBase.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import "CNLivePlayerDefines.h" | |
| 11 | + | |
| 12 | +NS_ASSUME_NONNULL_BEGIN | |
| 13 | + | |
| 14 | +@interface CNLiveStreamerBase : NSObject | |
| 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 音频编码器 (默认为AAC-HE) | |
| 43 | + @discussion audio codec used for encode | |
| 44 | + @see KSYAudioCodec | |
| 45 | + */ | |
| 46 | +@property (nonatomic, assign) CNLiveAudioCodec audioCodec; | |
| 47 | + | |
| 48 | +/** | |
| 49 | + @abstract 视频编码起始码率(单位:kbps) | |
| 50 | + @discussion 开始推流时的视频码率,开始推流后,根据网络情况在 [Min, Max]范围内调节 | |
| 51 | + @discussion 视频码率上调则画面更清晰,下调则画面更模糊 | |
| 52 | + */ | |
| 53 | +@property (nonatomic, assign) int videoInitBitrate; // kbit/s of video | |
| 54 | + | |
| 55 | +/** | |
| 56 | + @abstract 视频编码最高码率(单位:kbps) | |
| 57 | + @discussion 视频码率自适应调整的上限, 为目标码率 | |
| 58 | + */ | |
| 59 | +@property (nonatomic, assign) int videoMaxBitrate; // kbit/s of video | |
| 60 | + | |
| 61 | +/** | |
| 62 | + @abstract 视频编码最低码率(单位:kbps) | |
| 63 | + @discussion 视频码率自适应调整的下限 | |
| 64 | + */ | |
| 65 | +@property (nonatomic, assign) int videoMinBitrate; // kbit/s of video | |
| 66 | + | |
| 67 | +/** | |
| 68 | + @abstract 质量等级(默认:20) | |
| 69 | + @discussion 视频恒定质量等级,范围0~51,值越小,质量越好 | |
| 70 | + */ | |
| 71 | +@property (nonatomic, assign) int videoCrf; | |
| 72 | + | |
| 73 | +/** | |
| 74 | + @abstract 最大关键帧间隔(单位:秒, 默认:3) | |
| 75 | + @discussion 即GOP长度 画面静止时,隔n秒插入一个关键帧 | |
| 76 | + */ | |
| 77 | +@property (nonatomic, assign) float maxKeyInterval; // seconds | |
| 78 | + | |
| 79 | +/** | |
| 80 | + @abstract 音频编码码率(单位:kbps) | |
| 81 | + @discussion 音频目标编码码率 (比如48,96,128等) | |
| 82 | + */ | |
| 83 | +@property (nonatomic, assign) int audiokBPS; // kbit/s of audio | |
| 84 | + | |
| 85 | +/** | |
| 86 | + @abstract 带宽估计模式 | |
| 87 | + @discussion 带宽估计的策略选择 (开始推流前设置有效) | |
| 88 | + */ | |
| 89 | +@property (nonatomic, assign) CNLiveBWEstimateMode bwEstimateMode; | |
| 90 | + | |
| 91 | +/** | |
| 92 | + @abstract 本次直播的目标场景 (默认为KSYLiveScene_Default) | |
| 93 | + @discussion KSY内部会根据场景的特征进行参数调优,开始推流前设置有效 | |
| 94 | + */ | |
| 95 | +@property (nonatomic, assign) CNLiveLiveScene liveScene; | |
| 96 | + | |
| 97 | +/** | |
| 98 | + @abstract 本次录制的目标场景 (默认为KSYRecScene_ConstantBitRate) | |
| 99 | + @discussion 开始录制前设置有效 | |
| 100 | + */ | |
| 101 | +@property (nonatomic, assign) CNLiveRecScene recScene; | |
| 102 | + | |
| 103 | +/** | |
| 104 | + @abstract 视频编码性能档次 ( 默认为 CNLiveVideoEncodePer_LowPower) | |
| 105 | + @discussion 视频质量和设备资源之间的权衡,开始推流前设置有效 | |
| 106 | + */ | |
| 107 | +@property (nonatomic, assign) CNLiveVideoEncodePerformance videoEncodePerf; | |
| 108 | + | |
| 109 | +/** | |
| 110 | + @abstract 查询当前是否处于推流状态 (建立连接中, 或连接中) | |
| 111 | + */ | |
| 112 | +- (BOOL)isStreaming; | |
| 113 | + | |
| 114 | +/** | |
| 115 | + @abstract 静音推流 (仍然有音频输出发送, 只是音量为0) | |
| 116 | + @param bMute YES / ON | |
| 117 | + */ | |
| 118 | +- (void)muteStream:(BOOL)bMute; | |
| 119 | + | |
| 120 | +/** | |
| 121 | + @abstract 截图功能,目前只支持jpg格式 | |
| 122 | + @param jpegCompressionQuality 设置图像的压缩比例 | |
| 123 | + @param filename 图片的文件名 | |
| 124 | + */ | |
| 125 | +- (void)takePhotoWithQuality:(CGFloat)jpegCompressionQuality | |
| 126 | + fileName:(NSString *)filename; | |
| 127 | + | |
| 128 | +/** | |
| 129 | + @abstract 获取当前编码的截图 | |
| 130 | + @param completion 通过完成代码块获取到截图完成的图像 | |
| 131 | + */ | |
| 132 | +- (void)getSnapshotWithCompletion:(void (^)(UIImage*))completion; | |
| 133 | + | |
| 134 | +#pragma mark - 旁路录制 | |
| 135 | +/** | |
| 136 | + @abstract 旁路录像地址 | |
| 137 | + @discussion 开始录像后, 将直播的内容同步存储一份到本地文件 | |
| 138 | + eg: /private/var/mobile/Containers/Data/Application/APPID/tmp/test.mp4 | |
| 139 | + @discussion 如果只要存储本地文件,请继续使用原来的startStream接口 | |
| 140 | + @see hostURL, startStream | |
| 141 | + */ | |
| 142 | +@property (nonatomic, readonly) NSURL* bypassRecordURL; | |
| 143 | + | |
| 144 | +/** | |
| 145 | + @abstract 启动旁路录像 | |
| 146 | + @param url 本地录像文件地址:/private/var/..../test.mp4 | |
| 147 | + @return 是否能尝试启动写入, 不能表明真正开始录像了,真正开始请确认bypassRecordState的值 | |
| 148 | + @discussion 启动推流后才能开始写入文件,使用时请注意录制时长,避免内存不够 | |
| 149 | + @discussion 文件中的内容和直播内容完全一致 | |
| 150 | + */ | |
| 151 | +- (BOOL)startBypassRecord:(NSURL *)url; | |
| 152 | + | |
| 153 | +/** | |
| 154 | + @abstract 停止旁路录像 | |
| 155 | + */ | |
| 156 | +- (void)stopBypassRecord; | |
| 157 | + | |
| 158 | +/** 旁路录像的文件时长 */ | |
| 159 | +@property (nonatomic, readonly) double bypassRecordDuration; | |
| 160 | + | |
| 161 | +/** 旁路录像的状态 */ | |
| 162 | +@property (nonatomic, readonly) CNLiveRecordState bypassRecordState; | |
| 163 | + | |
| 164 | +/** 旁路录像的错误码 */ | |
| 165 | +@property (nonatomic, readonly) CNLiveRecordError bypassRecordErrorCode; | |
| 166 | + | |
| 167 | +/** 旁路录像的错误名称 */ | |
| 168 | +@property (nonatomic, readonly) NSString* bypassRecordErrorName; | |
| 169 | + | |
| 170 | +/** | |
| 171 | + @abstract 当旁路录制的状态变化时 | |
| 172 | + @discussion 只有设置 loop为NO时才有效, 在开始播放前设置有效 | |
| 173 | + */ | |
| 174 | +@property(nonatomic, copy) void(^bypassRecordStateChange)(CNLiveRecordState recordState); | |
| 175 | + | |
| 176 | +@end | |
| 177 | + | |
| 178 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerBase.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerBase.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveStreamerBase.h" | |
| 10 | +#import "CNLiveStreamerManager.h" | |
| 11 | + | |
| 12 | +#define STREAMER_Manager_Base [CNLiveStreamerManager manager].kit.streamerBase | |
| 13 | + | |
| 14 | +@implementation CNLiveStreamerBase | |
| 15 | +- (void)setVideoFPS:(int)videoFPS { | |
| 16 | + _videoFPS = videoFPS; | |
| 17 | + STREAMER_Manager_Base.videoFPS = videoFPS; | |
| 18 | +} | |
| 19 | + | |
| 20 | +- (void)setVideoMinFPS:(int)videoMinFPS { | |
| 21 | + _videoMinFPS = videoMinFPS; | |
| 22 | + STREAMER_Manager_Base.videoMinFPS = videoMinFPS; | |
| 23 | +} | |
| 24 | + | |
| 25 | +- (void)setVideoMaxFPS:(int)videoMaxFPS { | |
| 26 | + _videoMaxFPS = videoMaxFPS; | |
| 27 | + STREAMER_Manager_Base.videoMaxFPS = videoMaxFPS; | |
| 28 | +} | |
| 29 | + | |
| 30 | +- (void)setVideoCodec:(CNLiveVideoCodec)videoCodec { | |
| 31 | + _videoCodec = videoCodec; | |
| 32 | + STREAMER_Manager_Base.videoCodec = (KSYVideoCodec)videoCodec; | |
| 33 | +} | |
| 34 | + | |
| 35 | +- (void)setAudioCodec:(CNLiveAudioCodec)audioCodec { | |
| 36 | + _audioCodec = audioCodec; | |
| 37 | + STREAMER_Manager_Base.audioCodec = (KSYAudioCodec)audioCodec; | |
| 38 | +} | |
| 39 | + | |
| 40 | +- (void)setVideoInitBitrate:(int)videoInitBitrate { | |
| 41 | + _videoInitBitrate = videoInitBitrate; | |
| 42 | + STREAMER_Manager_Base.videoInitBitrate = videoInitBitrate; | |
| 43 | +} | |
| 44 | + | |
| 45 | +- (void)setVideoMaxBitrate:(int)videoMaxBitrate { | |
| 46 | + _videoMaxBitrate = videoMaxBitrate; | |
| 47 | + STREAMER_Manager_Base.videoMaxBitrate = videoMaxBitrate; | |
| 48 | +} | |
| 49 | + | |
| 50 | +- (void)setVideoMinBitrate:(int)videoMinBitrate { | |
| 51 | + _videoMinBitrate = videoMinBitrate; | |
| 52 | + STREAMER_Manager_Base.videoMinBitrate = videoMinBitrate; | |
| 53 | +} | |
| 54 | + | |
| 55 | +- (void)setVideoCrf:(int)videoCrf { | |
| 56 | + _videoCrf = videoCrf; | |
| 57 | + STREAMER_Manager_Base.videoCrf = videoCrf; | |
| 58 | +} | |
| 59 | + | |
| 60 | +- (void)setMaxKeyInterval:(float)maxKeyInterval { | |
| 61 | + _maxKeyInterval = maxKeyInterval; | |
| 62 | + STREAMER_Manager_Base.maxKeyInterval = maxKeyInterval; | |
| 63 | +} | |
| 64 | + | |
| 65 | +- (void)setAudiokBPS:(int)audiokBPS { | |
| 66 | + _audiokBPS = audiokBPS; | |
| 67 | + STREAMER_Manager_Base.audiokBPS = audiokBPS; | |
| 68 | +} | |
| 69 | + | |
| 70 | +- (void)setVideoEncodePerf:(CNLiveVideoEncodePerformance)videoEncodePerf { | |
| 71 | + _videoEncodePerf = videoEncodePerf; | |
| 72 | + STREAMER_Manager_Base.videoEncodePerf = (KSYVideoEncodePerformance)videoEncodePerf; | |
| 73 | +} | |
| 74 | + | |
| 75 | +- (BOOL)isStreaming { | |
| 76 | + return STREAMER_Manager_Base.isStreaming; | |
| 77 | +} | |
| 78 | + | |
| 79 | +- (NSString *)getStreamStateName:(CNLiveStreamState)state { | |
| 80 | + if (state > 4) { | |
| 81 | + return [STREAMER_Manager_Base getStreamStateName:(KSYStreamState)state]; | |
| 82 | + } | |
| 83 | + return nil; | |
| 84 | +} | |
| 85 | + | |
| 86 | +- (NSString *)getStreamErrorCodeName:(CNLiveStreamErrorCode)code { | |
| 87 | + return nil; | |
| 88 | +} | |
| 89 | + | |
| 90 | +- (void)muteStream:(BOOL)bMute { | |
| 91 | + [STREAMER_Manager_Base muteStream:bMute]; | |
| 92 | +} | |
| 93 | + | |
| 94 | +- (void)takePhotoWithQuality:(CGFloat)jpegCompressionQuality fileName:(NSString *)filename { | |
| 95 | + [STREAMER_Manager_Base takePhotoWithQuality:jpegCompressionQuality fileName:filename]; | |
| 96 | +} | |
| 97 | + | |
| 98 | +- (void)getSnapshotWithCompletion:(void (^)(UIImage *))completion { | |
| 99 | + if (completion) { | |
| 100 | + [STREAMER_Manager_Base getSnapshotWithCompletion:completion]; | |
| 101 | + } | |
| 102 | +} | |
| 103 | + | |
| 104 | +#pragma mark - 旁路录制 | |
| 105 | +- (NSURL *)bypassRecordURL { | |
| 106 | + return STREAMER_Manager_Base.bypassRecordURL; | |
| 107 | +} | |
| 108 | + | |
| 109 | +- (BOOL)startBypassRecord:(NSURL *)url { | |
| 110 | + return [STREAMER_Manager_Base startBypassRecord:url]; | |
| 111 | +} | |
| 112 | + | |
| 113 | +- (void)stopBypassRecord { | |
| 114 | + [STREAMER_Manager_Base stopBypassRecord]; | |
| 115 | +} | |
| 116 | + | |
| 117 | +- (double)bypassRecordDuration { | |
| 118 | + return STREAMER_Manager_Base.bypassRecordDuration; | |
| 119 | +} | |
| 120 | + | |
| 121 | +- (CNLiveRecordState)bypassRecordState { | |
| 122 | + return (CNLiveRecordState)STREAMER_Manager_Base.bypassRecordState; | |
| 123 | +} | |
| 124 | + | |
| 125 | +- (CNLiveRecordError)bypassRecordErrorCode { | |
| 126 | + return (CNLiveRecordError)STREAMER_Manager_Base.bypassRecordErrorCode; | |
| 127 | +} | |
| 128 | + | |
| 129 | +- (NSString *)bypassRecordErrorName { | |
| 130 | + return STREAMER_Manager_Base.bypassRecordErrorName; | |
| 131 | +} | |
| 132 | + | |
| 133 | +- (void)setBypassRecordStateChange:(void (^)(CNLiveRecordState))bypassRecordStateChange { | |
| 134 | + if (bypassRecordStateChange) { | |
| 135 | + STREAMER_Manager_Base.bypassRecordStateChange = ^(KSYRecordState recordState) { | |
| 136 | + bypassRecordStateChange((CNLiveRecordState)recordState); | |
| 137 | + }; | |
| 138 | + } | |
| 139 | +} | |
| 140 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerKit.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerKit.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/20. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <AVFoundation/AVFoundation.h> | |
| 11 | +#import <GPUImage/GPUImage.h> | |
| 12 | +#import "CNLivePlayerDefines.h" | |
| 13 | + | |
| 14 | +NS_ASSUME_NONNULL_BEGIN | |
| 15 | +@class CNLiveWatermark; | |
| 16 | +@class CNLiveBeautifyFaceFilter; | |
| 17 | +@class CNLiveAudioCapture; | |
| 18 | +@class CNLiveBgmPlayer; | |
| 19 | +@class CNLiveStreamerBase; | |
| 20 | + | |
| 21 | + | |
| 22 | +@interface CNLiveStreamerKit : NSObject | |
| 23 | +/** | |
| 24 | + @abstract 初始化(不会打断其他后台的音乐播放) | |
| 25 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 26 | + @param mode YES:纯音频推流 | |
| 27 | + @warning 必传参数 | |
| 28 | + */ | |
| 29 | +- (instancetype)initWithDefaultConfigWithPureAudioMode:(BOOL)mode; | |
| 30 | + | |
| 31 | +/** | |
| 32 | + @abstract 初始化(会打断其他后台的音乐播放) | |
| 33 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 34 | + @param mode YES:纯音频推流 | |
| 35 | + @warning 必传参数 | |
| 36 | + */ | |
| 37 | +- (instancetype)initWithInterruptConfigWithPureAudioMode:(BOOL)mode; | |
| 38 | + | |
| 39 | +/** | |
| 40 | + @abstract 活动ID,确保SP下的唯一性 | |
| 41 | + @discussion 必传参数 | |
| 42 | + */ | |
| 43 | +@property (nonatomic, strong) NSString *activityId; | |
| 44 | + | |
| 45 | +/** | |
| 46 | + @abstract 主播ID | |
| 47 | + @discussion 必传参数 | |
| 48 | + */ | |
| 49 | +@property (nonatomic, strong) NSString *channelId; | |
| 50 | + | |
| 51 | +/** | |
| 52 | + @abstract 是否开启控制台输出日志 | |
| 53 | + @discussion 默认为开启 | |
| 54 | + */ | |
| 55 | +@property (nonatomic, assign) BOOL logPrintEnable; | |
| 56 | + | |
| 57 | +/** | |
| 58 | + @abstract 屏幕方向,0:垂直即竖屏 /1:水平即横屏 ,默认为0 | |
| 59 | + */ | |
| 60 | +@property (nonatomic, assign) BOOL isHorizontalScreen; | |
| 61 | + | |
| 62 | +/** | |
| 63 | + @abstract 活动名称(非必传) | |
| 64 | + */ | |
| 65 | +@property (nonatomic, strong) NSString *activityName; | |
| 66 | + | |
| 67 | +/** | |
| 68 | + @abstract 活动状态,0:未开始 /1:已开始 /3:已结束 /4 已下线,默认为0(非必传) | |
| 69 | + */ | |
| 70 | +@property (nonatomic, strong) NSString *activityStatus; | |
| 71 | + | |
| 72 | +/** | |
| 73 | + @abstract 开始时间,格式yyyyMMddHHmmss(非必传) | |
| 74 | + */ | |
| 75 | +@property (nonatomic, strong) NSString *startTime; | |
| 76 | + | |
| 77 | +/** | |
| 78 | + @abstract 结束时间,格式yyyyMMddHHmmss(非必传) | |
| 79 | + */ | |
| 80 | +@property (nonatomic, strong) NSString *endTime; | |
| 81 | + | |
| 82 | +/** | |
| 83 | + @abstract 活动封面(非必传) | |
| 84 | + */ | |
| 85 | +@property (nonatomic, strong) NSString *coverImgUrl; | |
| 86 | + | |
| 87 | +/** | |
| 88 | + @abstract 活动分类ID(非必传) | |
| 89 | + */ | |
| 90 | +@property (nonatomic, strong) NSString *activityCategory; | |
| 91 | + | |
| 92 | +/** | |
| 93 | + @abstract 扩展字段,参数格式为Map对象的Json字符串(主播昵称、头像、)(非必传) | |
| 94 | + */ | |
| 95 | +@property (nonatomic, strong) NSString *extensions; | |
| 96 | + | |
| 97 | +/** | |
| 98 | + @abstract 回调通知活动状态(错误,结束、心跳异常等状态),post 参数activityid、activitystatus值...(非必传) | |
| 99 | + */ | |
| 100 | +@property (nonatomic, strong) NSString *callbackAcitvityStatushttpPostUrl; | |
| 101 | + | |
| 102 | +/** | |
| 103 | + @abstract 启动推流 | |
| 104 | + @warning 鉴权成功后开启推流(SDK内部创建活动并开启推流) | |
| 105 | + */ | |
| 106 | +- (void)startStream:(void (^)(void))successBlock failure:(void (^)(NSDictionary *errorDic))failureBlock; | |
| 107 | + | |
| 108 | +/** | |
| 109 | + @abstract 启动推流 | |
| 110 | + @param url 推流地址 | |
| 111 | + @param activityId 活动ID | |
| 112 | + @warning 后台创建活动完成,开启推流(仅视讯云平台有效) | |
| 113 | + */ | |
| 114 | +- (void)startStreamWithUrl:(NSString *)url activityId:(NSString *)activityId success:(void (^)(void))successBlock failure:(void (^)(NSDictionary *errorDic))failureBlock; | |
| 115 | + | |
| 116 | +/** | |
| 117 | + @abstract 暂停推流 | |
| 118 | + @warning 恢复推流时根据情况调用 startStream: 或 startStreamWithUrl:方法 | |
| 119 | + */ | |
| 120 | +- (void)pauseStreaming; | |
| 121 | + | |
| 122 | +/** | |
| 123 | + * 主播端未停止推流,但是服务端认为该视频不合法,断开推流,接收到该通知主播端应结束推流(视讯云平台有效),弃用 | |
| 124 | + */ | |
| 125 | +FOUNDATION_EXPORT NSString *const CNLiveServiceConnectBreakNotification NS_AVAILABLE_IOS(7_0); | |
| 126 | + | |
| 127 | +#pragma mark - 普通推流 | |
| 128 | + | |
| 129 | +/** | |
| 130 | + @abstract 初始化(不会打断其他后台的音乐播放) | |
| 131 | + @param mode YES:纯音频推流 | |
| 132 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 133 | + */ | |
| 134 | +- (instancetype)initWithDefaultConfigWithPureAudioMode:(BOOL)mode; | |
| 135 | + | |
| 136 | +/** | |
| 137 | + @abstract 初始化(会打断其他后台的音乐播放) | |
| 138 | + @param mode YES:纯音频推流 | |
| 139 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 140 | + */ | |
| 141 | +- (instancetype)initWithInterruptConfigWithPureAudioMode:(BOOL)mode; | |
| 142 | + | |
| 143 | +/** | |
| 144 | + * 预览视图 | |
| 145 | + */ | |
| 146 | +@property (nonatomic, readonly) UIView *preview; | |
| 147 | + | |
| 148 | +/** | |
| 149 | + @abstract 获取水印工具 | |
| 150 | + @discussion 通过它来设置水印图片/文字 | |
| 151 | + */ | |
| 152 | +@property (nonatomic, readonly) CNLiveWatermark *watermark; | |
| 153 | + | |
| 154 | +/** | |
| 155 | + @abstract 获取底层推流工具 | |
| 156 | + @discussion 通过它来设置推流参数 | |
| 157 | + */ | |
| 158 | +@property (nonatomic, readonly) CNLiveStreamerBase *streamerBase; | |
| 159 | + | |
| 160 | +/** | |
| 161 | + @abstract 获取音频采集工具 | |
| 162 | + */ | |
| 163 | +@property (nonatomic, readonly) CNLiveAudioCapture *audioCapture; | |
| 164 | + | |
| 165 | +/** | |
| 166 | + @abstract 获取美颜工具 | |
| 167 | + */ | |
| 168 | +@property (nonatomic, readonly) CNLiveBeautifyFaceFilter *beautifyFaceFilter; | |
| 169 | + | |
| 170 | +/** | |
| 171 | + @abstract 背景音乐工具 | |
| 172 | + */ | |
| 173 | +@property (nonatomic, readonly) CNLiveBgmPlayer *bgmPlayer; | |
| 174 | + | |
| 175 | +#pragma mark - capture & preview settings | |
| 176 | + | |
| 177 | +/** | |
| 178 | + @abstract 采集分辨率 (仅在开始采集前设置有效) | |
| 179 | + @discussion 参见iOS的 AVCaptureSessionPresetXXX的定义 默认值为AVCaptureSessionPreset640x480 | |
| 180 | + @discussion 不同设备支持的预设分辨率可能不同, 请尽量与预览分辨率一致 | |
| 181 | + */ | |
| 182 | +@property (nonatomic, assign) NSString * capPreset; | |
| 183 | + | |
| 184 | +/** | |
| 185 | + @abstract 查询实际的采集分辨率 | |
| 186 | + @discussion 参见iOS的 AVCaptureSessionPresetXXX的定义 | |
| 187 | + */ | |
| 188 | +- (CGSize)captureDimension; | |
| 189 | + | |
| 190 | +/** | |
| 191 | + @abstract 预览分辨率 (仅在开始采集前设置有效) | |
| 192 | + @discussion 内部始终将较大的值作为宽度 (若需要竖屏,请设置 videoOrientation) | |
| 193 | + @discussion 宽高都会向上取整为4的整数倍 | |
| 194 | + @discussion 有效范围: 宽度[160, 1920] 高度[ 90, 1080], 超出范围会取边界有效值 | |
| 195 | + @discussion 当预览分辨率与采集分辨率不一致时: | |
| 196 | + 若宽高比不同, 先进行裁剪, 再进行缩放 | |
| 197 | + 若宽高比相同, 直接进行缩放 | |
| 198 | + @discussion 默认值为(640, 360) | |
| 199 | + */ | |
| 200 | +@property (nonatomic, assign) CGSize previewDimension; | |
| 201 | + | |
| 202 | +/** | |
| 203 | + @abstract 用户定义的视频 **推流** 分辨率 | |
| 204 | + @discussion 有效范围: 宽度[160, 1280] 高度[ 90, 720], 超出范围会取边界有效值 | |
| 205 | + @discussion 其他与previewDimension限定一致, | |
| 206 | + @discussion 当与previewDimension不一致时, 同样先裁剪到相同宽高比, 再进行缩放 | |
| 207 | + @discussion 默认值为(640, 360) | |
| 208 | + @see previewDimension | |
| 209 | + */ | |
| 210 | +@property (nonatomic, assign) CGSize streamDimension; | |
| 211 | + | |
| 212 | +/** | |
| 213 | + @abstract 采集及编码视频帧率 (开始采集前设置有效) | |
| 214 | + @discussion video frame per seconds 有效范围[1~30], 超出范围会取边界有效值 | |
| 215 | + @discussion 默认值为15 */ | |
| 216 | +@property (nonatomic, assign) int videoFPS; | |
| 217 | + | |
| 218 | +/** | |
| 219 | + @abstract 摄像头位置 | |
| 220 | + @discussion 前后摄像头 默认为前摄像头 | |
| 221 | + */ | |
| 222 | +@property (nonatomic, assign) AVCaptureDevicePosition cameraPosition; | |
| 223 | + | |
| 224 | + | |
| 225 | +/** | |
| 226 | + @abstract 获取当前采集设备的指针 | |
| 227 | + @return AVCaptureDevice* 预览开始前调用返回为nil,开始预览后,返回当前正在使用的摄像头 | |
| 228 | + @warning 请勿修改摄像头的像素格式,帧率,分辨率等参数,修改后会导致推流工作异常或崩溃 | |
| 229 | + */ | |
| 230 | +- (AVCaptureDevice *) getCurrentCameraDevices; | |
| 231 | + | |
| 232 | +/** | |
| 233 | + @abstract 采集设备状态 | |
| 234 | + */ | |
| 235 | +@property (nonatomic, readonly) CNLiveCaptureState captureState; | |
| 236 | + | |
| 237 | +FOUNDATION_EXPORT NSString *const CNLiveCaptureStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 238 | + | |
| 239 | +#pragma mark - stream settings | |
| 240 | + | |
| 241 | +/** | |
| 242 | + @abstract 推流状态 | |
| 243 | + */ | |
| 244 | +@property (nonatomic, readonly) CNLiveStreamState streamState; | |
| 245 | + | |
| 246 | +FOUNDATION_EXPORT NSString *const CNLiveStreamStateDidChangeNotification NS_AVAILABLE_IOS(7_0); | |
| 247 | + | |
| 248 | +/** | |
| 249 | + * @abstract 推流错误码,用于指示推流失败的原因 | |
| 250 | + */ | |
| 251 | +@property (nonatomic, readonly) CNLiveStreamErrorCode streamErrorCode; | |
| 252 | + | |
| 253 | +#pragma mark - filters | |
| 254 | +/** | |
| 255 | + @abstract 设置当前使用的滤镜 | |
| 256 | + @discussion 若filter 为nil, 则关闭滤镜 | |
| 257 | + @discussion 若filter 为GPUImageFilter的实例,则使用该滤镜做处理 | |
| 258 | + @discussion filter 也可以是GPUImageFilterGroup的实例,可以将多个滤镜组合 | |
| 259 | + */ | |
| 260 | +- (void)setupFilter:(GPUImageOutput<GPUImageInput>*) filter; | |
| 261 | + | |
| 262 | +#pragma mark - reconnect | |
| 263 | +/** | |
| 264 | + @abstract 自动重连次数 关闭(0), 开启(>0), 默认为0 | |
| 265 | + @discussion 当内部发现推流错误后, 会在一段时间后尝试重连 | |
| 266 | + 自动重连不会重新获取推流地址, 仍然使用上次推流的地址 | |
| 267 | + @warning 如果在推流地址有过期时间, 请勿开启 | |
| 268 | + */ | |
| 269 | +@property (nonatomic, assign) int maxAutoRetry; | |
| 270 | + | |
| 271 | +/** | |
| 272 | + @abstract 自动重连延时, 发现连接错误后, 重试的延时 | |
| 273 | + @discussion 单位为秒, 默认为2s, 最小值为0.1s | |
| 274 | + */ | |
| 275 | +@property (nonatomic, assign) double autoRetryDelay; | |
| 276 | + | |
| 277 | +/** | |
| 278 | + @abstract 视讯云平台下重连状态(普通推流无效) | |
| 279 | + */ | |
| 280 | +@property (nonatomic, assign) CNLiveReconnectStatus reconnectStatus; | |
| 281 | + | |
| 282 | +/** | |
| 283 | + @abstract 自动重连失败通知(注*仅视讯云平台有效,普通方法推流自动重连失败收不到该通知) | |
| 284 | + @discussion 仅推流错误码为 CNLiveStreamErrorCode_CONNECT_BREAK、 | |
| 285 | + CNLiveStreamErrorCode_AV_SYNC_ERROR、 | |
| 286 | + CNLiveStreamErrorCode_Connect_Server_failed、 | |
| 287 | + CNLiveStreamErrorCode_DNS_Parse_failed、 | |
| 288 | + CNLiveStreamErrorCode_CODEC_OPEN_FAILED、 | |
| 289 | + CNLiveStreamErrorCode_ABNORMAL 时为可重连状态; | |
| 290 | + */ | |
| 291 | +FOUNDATION_EXPORT NSString *const CNLiveStreamReconnectStatusChangedNotification NS_AVAILABLE_IOS(7_0); | |
| 292 | + | |
| 293 | +/** | |
| 294 | + @abstract 切换摄像头 | |
| 295 | + @return TRUE: 成功切换摄像头,开启预览后设置有效 | |
| 296 | + */ | |
| 297 | +- (BOOL)switchCamera; | |
| 298 | + | |
| 299 | +/** | |
| 300 | + @abstract 当前采集设备是否支持闪光灯 | |
| 301 | + @return YES / NO | |
| 302 | + @discussion 通常只有后置摄像头支持闪光灯 | |
| 303 | + */ | |
| 304 | +- (BOOL)isTorchSupported; | |
| 305 | + | |
| 306 | +/** | |
| 307 | + @abstract 开关闪光灯 | |
| 308 | + @discussion 切换闪光灯的开关状态 开 <--> 关 | |
| 309 | + @warning 对后摄像头设置有效 | |
| 310 | + */ | |
| 311 | +- (void)toggleTorch; | |
| 312 | +- (void)setTorchMode:(AVCaptureTorchMode)mode; | |
| 313 | + | |
| 314 | +/** | |
| 315 | + @abstract 摄像头朝向 | |
| 316 | + @discussion 参见UIInterfaceOrientation | |
| 317 | + @discussion 竖屏时: width < height | |
| 318 | + @discussion 横屏时: width > height | |
| 319 | + @discussion 需要与UI方向一致 | |
| 320 | + */ | |
| 321 | +- (void)setVideoOrientationBy:(UIInterfaceOrientation)uiOrien; | |
| 322 | + | |
| 323 | +/** | |
| 324 | + @abstract 启动预览 | |
| 325 | + @param view 预览画面作为subview,插入到 view 的最底层 | |
| 326 | + @discussion 设置完成采集参数之后,按照设置值启动预览,启动后对采集参数修改不会生效 | |
| 327 | + @discussion 需要访问摄像头和麦克风的权限,若授权失败,其他API都会拒绝服务 | |
| 328 | + @warning: 开始推流前必须先启动预览 | |
| 329 | + */ | |
| 330 | +- (void)startPreview:(UIView*) view; | |
| 331 | + | |
| 332 | +/** | |
| 333 | + @abstract 停止预览,停止采集设备,并清理会话 | |
| 334 | + @warning 若推流未结束,则先停止推流 | |
| 335 | + */ | |
| 336 | +- (void)stopPreview; | |
| 337 | + | |
| 338 | +/** | |
| 339 | + @abstract 启动推流 | |
| 340 | + @param hostURL rtmp 服务器地址 “rtmp://xxx.xxx.xxx.xxx/appname/streamKey" | |
| 341 | + */ | |
| 342 | +- (void)startStream:(NSURL *)hostURL; | |
| 343 | + | |
| 344 | +/** | |
| 345 | + @abstract 停止推流 | |
| 346 | + */ | |
| 347 | +- (void)stopStream; | |
| 348 | + | |
| 349 | +/** | |
| 350 | + @abstract 开始录制本地视频(不支持边推边录) | |
| 351 | + @param path 本地存储路径 | |
| 352 | + */ | |
| 353 | +- (void)startRecordWithFilePath:(NSString *)path; | |
| 354 | + | |
| 355 | +/** | |
| 356 | + @abstract 停止录制本地视频 | |
| 357 | + @discussion 与 - (void)startRecordWithFilePath: 搭配使用 | |
| 358 | + */ | |
| 359 | +- (void)stopRecord; | |
| 360 | + | |
| 361 | +/** | |
| 362 | + @abstract 进入后台: 暂停图像采集 | |
| 363 | + @discussion 暂停图像采集和预览 | |
| 364 | + */ | |
| 365 | +- (void)appEnterBackground; | |
| 366 | + | |
| 367 | +/** | |
| 368 | + @abstract 回到前台: 恢复采集 | |
| 369 | + @discussion 恢复图像采集和预览 | |
| 370 | + */ | |
| 371 | +- (void)appBecomeActive; | |
| 372 | + | |
| 373 | +/** | |
| 374 | + @abstract 退出推流页面时调用 | |
| 375 | + */ | |
| 376 | +- (void)quitStreaming; | |
| 377 | + | |
| 378 | +#pragma mark - mirror & rotate | |
| 379 | + | |
| 380 | +/** | |
| 381 | + @abstract 预览设置成镜像模式,默认为NO | |
| 382 | + */ | |
| 383 | +@property (nonatomic, assign) BOOL previewMirrored; | |
| 384 | + | |
| 385 | +/** | |
| 386 | + @abstract 推流设置成镜像模式,默认为NO | |
| 387 | + */ | |
| 388 | +@property (nonatomic, assign) BOOL streamerMirrored; | |
| 389 | + | |
| 390 | +/** | |
| 391 | + @abstract 根据UI的朝向旋转预览视图, 保证预览视图全屏铺满窗口 | |
| 392 | + @param orie 旋转到目标朝向, 需要从demo中获取UI的朝向传入 | |
| 393 | + @discussion 采集到的图像的朝向还是和启动时的朝向一致 | |
| 394 | + */ | |
| 395 | +- (void) rotatePreviewTo: (UIInterfaceOrientation) orie; | |
| 396 | + | |
| 397 | +/** | |
| 398 | + @abstract 根据UI的朝向旋转推流画面 | |
| 399 | + @param orie 旋转到目标朝向, 需要从demo中获取UI的朝向传入 | |
| 400 | + */ | |
| 401 | +- (void) rotateStreamTo: (UIInterfaceOrientation) orie; | |
| 402 | + | |
| 403 | +/** | |
| 404 | + @abstract 当前采集设备是否支持自动变焦 | |
| 405 | + @param point 相机对焦的位置 | |
| 406 | + */ | |
| 407 | +- (void)focusAtPoint:(CGPoint )point; | |
| 408 | + | |
| 409 | +/** | |
| 410 | + @abstract 当前采集设备是否支持自动曝光 | |
| 411 | + @param point 相机曝光的位置 | |
| 412 | + */ | |
| 413 | +- (void)exposureAtPoint:(CGPoint )point; | |
| 414 | + | |
| 415 | +/** | |
| 416 | + @abstract 触摸缩放因子 | |
| 417 | + */ | |
| 418 | +@property (nonatomic, assign) CGFloat pinchZoomFactor; | |
| 419 | + | |
| 420 | +/** | |
| 421 | + @abstract 摄像头防抖模式,切换摄像头后需要进行重新设置 | |
| 422 | + @discussion (iPhone前置摄像头不支持防抖功能) | |
| 423 | + */ | |
| 424 | +@property (nonatomic, assign) AVCaptureVideoStabilizationMode stabilizationMode; | |
| 425 | + | |
| 426 | +/** | |
| 427 | + * 悬浮窗 | |
| 428 | + */ | |
| 429 | +- (UIView *)floatingWindowFrame:(CGRect)rect; | |
| 430 | + | |
| 431 | +@end | |
| 432 | + | |
| 433 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerKit.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerKit.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/20. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveStreamerKit.h" | |
| 10 | +#import "GCDAsyncSocket.h" | |
| 11 | +#import <CNLiveMsgTools/CNLiveMsgManager.h> | |
| 12 | + | |
| 13 | +#import "CNLiveStreamerManager.h" | |
| 14 | +#import "CNLiveWatermark.h" | |
| 15 | +#import "CNLiveStreamerBase.h" | |
| 16 | +#import "CNLiveAudioCapture.h" | |
| 17 | +#import "CNLiveBeautifyFaceFilter.h" | |
| 18 | +#import "CNLiveBgmPlayer.h" | |
| 19 | +#import "CNLivePlayerManager.h" | |
| 20 | +#import "CNLiveStreamerTools.h" | |
| 21 | + | |
| 22 | +//采集状态通知 | |
| 23 | +NSString * const CNLiveCaptureStateDidChangeNotification = @"CNLiveCaptureStateDidChangeNotification"; | |
| 24 | + | |
| 25 | +//推流状态通知 | |
| 26 | +NSString * const CNLiveStreamStateDidChangeNotification = @"CNLiveStreamStateDidChangeNotification"; | |
| 27 | + | |
| 28 | +//自动重连失败通知 | |
| 29 | +NSString * const CNLiveStreamReconnectStatusChangedNotification = @"CNLiveStreamReconnectStatusChangedNotification"; | |
| 30 | + | |
| 31 | +//获取直播时长通知 | |
| 32 | +NSString * const CNLiveStreamGetDurationNotification = @"CNLiveStreamGetDurationNotification"; | |
| 33 | + | |
| 34 | +//收到消息通知 | |
| 35 | +NSString * const MsgToolsStreamReceiveNotification = @"CNLiveMsgToolsPlayerReceiveNotification"; | |
| 36 | + | |
| 37 | +//由服务端断开 | |
| 38 | +NSString * const CNLiveServiceConnectBreakNotification = @"CNLiveServiceConnectBreakNotification"; | |
| 39 | + | |
| 40 | +@interface CNLiveStreamerKit()<GCDAsyncSocketDelegate> { | |
| 41 | + BOOL _isRecording; //是否正在直播 | |
| 42 | + BOOL _activitySuccess; //活动是否创建成功 | |
| 43 | + BOOL _isCNLiveStreaming; //是否为视讯云平台推流 | |
| 44 | + | |
| 45 | + BOOL _noti; | |
| 46 | + BOOL _pureAudioMode; //是否为纯音频推流 | |
| 47 | + | |
| 48 | + int _autoRetryCnt; //记录重试次数 | |
| 49 | + BOOL _retryFailed; //重连失败 | |
| 50 | + BOOL _reteying; //是否处于重试中 | |
| 51 | + BOOL _initiativeRetry; //用户主动重试 | |
| 52 | + | |
| 53 | + BOOL _registerChatNoti; //记录是否已经注册了聊天室通知 | |
| 54 | + NSString *_contentId; | |
| 55 | + NSString *_roomId; | |
| 56 | + int _originalStatus; //原始状态 | |
| 57 | + CNLiveStreamerManager *_manager; | |
| 58 | + | |
| 59 | + BOOL _enterBackground; | |
| 60 | +} | |
| 61 | + | |
| 62 | +@property (nonatomic, strong) GPUImageFilter *filter; //美颜(用于默认美颜) | |
| 63 | +@property (nonatomic, strong) NSURL *hostURL; //推流地址 | |
| 64 | +@property (nonatomic, strong) NSMutableDictionary *parameter; //存储直播活动相关信息 | |
| 65 | +@property (nonatomic, strong) GCDAsyncSocket *socket; //socket用于向服务器发送消息(30s一次) | |
| 66 | +@property (nonatomic, retain) NSTimer *heartTimer; //心跳定时器 | |
| 67 | + | |
| 68 | +@property (nonatomic, strong) CNLiveWatermark *watermark; //水印 | |
| 69 | +@property (nonatomic, strong) CNLiveStreamerBase *streamerBase; //推流 | |
| 70 | +@property (nonatomic, strong) CNLiveAudioCapture *audioCapture; //音频采集 | |
| 71 | +@property (nonatomic, strong) CNLiveBeautifyFaceFilter *beautifyFaceFilter; //美颜 | |
| 72 | +@property (nonatomic, strong) CNLiveBgmPlayer *bgmPlayer; //背景音乐 | |
| 73 | + | |
| 74 | +@end | |
| 75 | + | |
| 76 | +@implementation CNLiveStreamerKit | |
| 77 | +/** | |
| 78 | + @abstract 初始化(不会打断其他后台的音乐播放) | |
| 79 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 80 | + @param mode YES:纯音频推流 | |
| 81 | + @warning 必传参数 | |
| 82 | + */ | |
| 83 | +- (instancetype)initWithDefaultConfigWithPureAudioMode:(BOOL)mode{ | |
| 84 | + if (self = [super init]) { | |
| 85 | + _logPrintEnable = YES; | |
| 86 | + _pureAudioMode = mode; | |
| 87 | + _autoRetryCnt = 0; | |
| 88 | + _autoRetryDelay = 2.0; | |
| 89 | + | |
| 90 | +// _parameter = [[NSMutableDictionary alloc] init]; | |
| 91 | + [self setStreamerCfgWithInterrupt:NO]; | |
| 92 | +// | |
| 93 | +// //视讯云直播,注册前后台切换通知 | |
| 94 | +// [self addStatusChangedObservers]; | |
| 95 | + | |
| 96 | + } | |
| 97 | + return self; | |
| 98 | +} | |
| 99 | + | |
| 100 | +/** | |
| 101 | + @abstract 初始化(会打断其他后台的音乐播放) | |
| 102 | + @warning 实例化的对象必须赋值给一个全局变量 | |
| 103 | + @param mode YES:纯音频推流 | |
| 104 | + @warning 必传参数 | |
| 105 | + */ | |
| 106 | +- (instancetype)initWithInterruptConfigWithPureAudioMode:(BOOL)mode{ | |
| 107 | + if (self = [super init]) { | |
| 108 | + _logPrintEnable = YES; | |
| 109 | + _pureAudioMode = mode; | |
| 110 | + _autoRetryCnt = 0; | |
| 111 | + _autoRetryDelay = 2.0; | |
| 112 | + | |
| 113 | +// _parameter = [[NSMutableDictionary alloc] init]; | |
| 114 | + [self setStreamerCfgWithInterrupt:YES]; | |
| 115 | +// | |
| 116 | +// //视讯云直播,注册前后台切换通知 | |
| 117 | +// [self addStatusChangedObservers]; | |
| 118 | + | |
| 119 | + } | |
| 120 | + return self; | |
| 121 | +} | |
| 122 | + | |
| 123 | +#pragma mark - 初始化KSY | |
| 124 | +- (void)setStreamerCfgWithInterrupt:(BOOL)interrupt { | |
| 125 | + _manager = [CNLiveStreamerManager manager]; | |
| 126 | + | |
| 127 | + [UIApplication sharedApplication].idleTimerDisabled = YES; | |
| 128 | + | |
| 129 | + if (interrupt) { //初始化金山kit | |
| 130 | + _manager.kit = [[KSYGPUStreamerKit alloc] initWithInterruptCfg]; | |
| 131 | + } | |
| 132 | + else { | |
| 133 | + _manager.kit = [[KSYGPUStreamerKit alloc] initWithDefaultCfg]; | |
| 134 | + } | |
| 135 | + | |
| 136 | + if (_pureAudioMode) { //是否为纯音频推流 | |
| 137 | + [_manager.kit.aCapDev startCapture]; | |
| 138 | + _manager.kit.streamerBase.bWithVideo = NO; | |
| 139 | + | |
| 140 | + } | |
| 141 | + else { | |
| 142 | + //设置初始美颜 | |
| 143 | + _filter = [[KSYGPUBeautifyExtFilter alloc] init]; | |
| 144 | + [(KSYGPUBeautifyExtFilter *)_filter setBeautylevel: 0.5]; | |
| 145 | + [_manager.kit setupFilter:_filter]; | |
| 146 | + | |
| 147 | + //摄像头位置 | |
| 148 | + _manager.kit.cameraPosition = AVCaptureDevicePositionFront; | |
| 149 | + } | |
| 150 | + | |
| 151 | + _manager.kit.streamerBase.shouldEnableKSYStatModule = NO; //收集网络相关状态的日志(不收集) | |
| 152 | + _manager.kit.videoProcessingCallback = ^(CMSampleBufferRef sampleBuffer){ | |
| 153 | + | |
| 154 | + }; | |
| 155 | + | |
| 156 | + _manager.kit.audioProcessingCallback = ^(CMSampleBufferRef buf){ | |
| 157 | + | |
| 158 | + }; | |
| 159 | + | |
| 160 | + //添加通知 | |
| 161 | +// [self addKSYObservers]; | |
| 162 | + | |
| 163 | +} | |
| 164 | + | |
| 165 | +#pragma mark - 通知 | |
| 166 | +- (void)addKSYObservers { | |
| 167 | + if (_noti) return; | |
| 168 | + _noti = YES; | |
| 169 | + | |
| 170 | + //采集状态通知 | |
| 171 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 172 | + selector:@selector(onCaptureStateChange) | |
| 173 | + name:KSYCaptureStateDidChangeNotification | |
| 174 | + object:nil]; | |
| 175 | + | |
| 176 | + //推流状态通知 | |
| 177 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 178 | + selector:@selector(onStreamStateChange) | |
| 179 | + name:KSYStreamStateDidChangeNotification | |
| 180 | + object:nil]; | |
| 181 | + | |
| 182 | + | |
| 183 | +} | |
| 184 | + | |
| 185 | +//前后台切换通知 | |
| 186 | +- (void)addStatusChangedObservers { | |
| 187 | + | |
| 188 | + if (_isCNLiveStreaming) return; | |
| 189 | + | |
| 190 | + _isCNLiveStreaming = YES; | |
| 191 | + | |
| 192 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 193 | + selector:@selector(enterBackground) | |
| 194 | + name:UIApplicationWillResignActiveNotification | |
| 195 | + object:nil]; | |
| 196 | + | |
| 197 | + [[NSNotificationCenter defaultCenter] addObserver:self | |
| 198 | + selector:@selector(enterForeground) | |
| 199 | + name:UIApplicationDidBecomeActiveNotification | |
| 200 | + object:nil]; | |
| 201 | +} | |
| 202 | + | |
| 203 | +- (void)onCaptureStateChange { | |
| 204 | + _captureState = (CNLiveCaptureState)_manager.kit.captureState; | |
| 205 | + | |
| 206 | + if (self.logPrintEnable) { | |
| 207 | + NSLog(@"CNLiveGPUStreamerKit-->采集设备工作状态CNLiveCaptureState:%ld", (unsigned long)self.captureState); | |
| 208 | + } | |
| 209 | + | |
| 210 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveCaptureStateDidChangeNotification | |
| 211 | + object:nil | |
| 212 | + userInfo:nil]; | |
| 213 | +} | |
| 214 | + | |
| 215 | +- (void)onStreamStateChange { | |
| 216 | + //推流状态发生改变时通知开发者 | |
| 217 | + if (_manager.kit.streamerBase.streamState == KSYStreamStateError) { | |
| 218 | + _streamErrorCode = (CNLiveStreamErrorCode)_manager.kit.streamerBase.streamErrorCode; | |
| 219 | + } | |
| 220 | + _streamState = (CNLiveStreamState)_manager.kit.streamerBase.streamState; | |
| 221 | + | |
| 222 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamStateDidChangeNotification | |
| 223 | + object:nil | |
| 224 | + userInfo:nil]; | |
| 225 | + | |
| 226 | + if (_manager.kit.streamerBase.streamState == KSYStreamStateConnected) { //连接成功恢复连接次数最大值 | |
| 227 | + | |
| 228 | + if (_autoRetryCnt < 3) { | |
| 229 | + _reconnectStatus = CNLiveReconnectStatusSuccess; | |
| 230 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamReconnectStatusChangedNotification | |
| 231 | + object:nil | |
| 232 | + userInfo:nil]; | |
| 233 | + [self recoverReconnectConfig]; | |
| 234 | + } | |
| 235 | + _autoRetryCnt = _maxAutoRetry; | |
| 236 | + } | |
| 237 | + | |
| 238 | + //推流出错 | |
| 239 | + if (_manager.kit.streamerBase.streamState == KSYStreamStateError && _isCNLiveStreaming) { | |
| 240 | + _isRecording = NO; | |
| 241 | + | |
| 242 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 243 | +// [self requestStopStreamerWithInfo:[NSString stringWithFormat:@"KSY error:%@",[_manager.kit.streamerBase getKSYStreamErrorCodeName:_manager.kit.streamerBase.streamErrorCode]] timeString:timeString]; | |
| 244 | + }]; | |
| 245 | + | |
| 246 | + [CNLiveStreamerTools errorStat:@"推流出错" activityId:_activityId]; | |
| 247 | + | |
| 248 | + //重连失败通知开发者 | |
| 249 | + if (_retryFailed && _isCNLiveStreaming) { | |
| 250 | + | |
| 251 | + _reconnectStatus = CNLiveReconnectStatusFailed; | |
| 252 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamReconnectStatusChangedNotification | |
| 253 | + object:nil | |
| 254 | + userInfo:nil]; | |
| 255 | + [self recoverReconnectConfig]; | |
| 256 | + } | |
| 257 | + | |
| 258 | + //以下为视讯云平台重连情况 | |
| 259 | + if (_isCNLiveStreaming) { | |
| 260 | + if (_manager.kit.streamerBase.streamErrorCode == KSYStreamErrorCode_CONNECT_BREAK || | |
| 261 | + _manager.kit.streamerBase.streamErrorCode == KSYStreamErrorCode_AV_SYNC_ERROR || | |
| 262 | + _manager.kit.streamerBase.streamErrorCode == KSYStreamErrorCode_Connect_Server_failed || | |
| 263 | + _manager.kit.streamerBase.streamErrorCode == KSYStreamErrorCode_DNS_Parse_failed || | |
| 264 | + _manager.kit.streamerBase.streamErrorCode == KSYStreamErrorCode_CODEC_OPEN_FAILED) { | |
| 265 | + | |
| 266 | + // NSLog(@"ksyError---->autoRetryCnt:%d----initiativeRetry:%@----reteying:%@----retryFailed:%@", _autoRetryCnt, _initiativeRetry ? @"YES" : @"NO", _reteying?@"YES":@"NO", _retryFailed?@"YES":@"NO"); | |
| 267 | + | |
| 268 | + if (_autoRetryCnt>0 && !_initiativeRetry) { | |
| 269 | + [self tryReconnect]; | |
| 270 | + // NSLog(@"CNLiveGPUStreamerKit-->reconnect ----- 金山ERROR"); | |
| 271 | + | |
| 272 | + } | |
| 273 | + } | |
| 274 | + | |
| 275 | + } | |
| 276 | + | |
| 277 | + if (self.logPrintEnable) { | |
| 278 | + NSLog(@"CNLiveGPUStreamerKit-->推流出错CNLiveStreamErrorCode error:%ld", (unsigned long)self.streamErrorCode); | |
| 279 | + } | |
| 280 | + | |
| 281 | + } else if ( _manager.kit.streamerBase.streamState == KSYStreamStateDisconnecting) {//断开推流 | |
| 282 | + | |
| 283 | + if (![CNLiveStreamerTools isEmptyString:PLAYER_AppID] && _enterBackground == NO && _isRecording) { | |
| 284 | + _streamErrorCode = CNLiveStreamErrorCode_ABNORMAL; | |
| 285 | + _streamState = CNLiveStreamStateError; | |
| 286 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamStateDidChangeNotification | |
| 287 | + object:nil | |
| 288 | + userInfo:nil]; | |
| 289 | + | |
| 290 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 291 | +// [self requestStopStreamerWithInfo:@"CNLiveStreamErrorCode_ABNORMAL_2" timeString:timeString]; | |
| 292 | + }]; | |
| 293 | + | |
| 294 | + _isRecording = NO; | |
| 295 | + if (self.logPrintEnable) { | |
| 296 | + NSLog(@"CNLiveGPUStreamerKit-->推流出错CNLiveStreamErrorCode error:%ld", (unsigned long)self.streamErrorCode); | |
| 297 | + } | |
| 298 | + } | |
| 299 | + } else { | |
| 300 | + } | |
| 301 | + | |
| 302 | + NSLog(@"CNLiveGPUStreamerKit-->推流工作状态发生变化CNLiveStreamState:%ld", (unsigned long)self.streamState); | |
| 303 | + | |
| 304 | +} | |
| 305 | + | |
| 306 | +- (void)enterBackground { | |
| 307 | + // [_manager.kit.aCapDev stopCapture]; | |
| 308 | + // NSLog(@"CNLiveGPUStreamerKit-->关闭音频采集状态"); | |
| 309 | + | |
| 310 | + /** | |
| 311 | + * 视讯云平台:推流过程中退到后台或者锁屏情况下,需将活动状态改为“-2”,以便观众端收到主播离开的消息 | |
| 312 | + */ | |
| 313 | + if (!_activitySuccess) return; | |
| 314 | + _enterBackground = YES; | |
| 315 | + [_manager.kit.streamerBase stopStream]; | |
| 316 | + | |
| 317 | + __block UIBackgroundTaskIdentifier task; | |
| 318 | + task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ | |
| 319 | + [[UIApplication sharedApplication] endBackgroundTask:task]; | |
| 320 | + }]; | |
| 321 | + | |
| 322 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 323 | +// [self requestStopStreamerWithInfo:@"CNLiveStreamErrorCode_EnterBackground" timeString:timeString]; | |
| 324 | + }]; | |
| 325 | +} | |
| 326 | + | |
| 327 | +- (void)enterForeground { | |
| 328 | + | |
| 329 | + // BOOL enterForeground = [_manager.kit.aCapDev startCapture]; | |
| 330 | + // NSLog(@"CNLiveGPUStreamerKit-->重新开启音频采集状态,ret:%@",enterForeground ? @"YES" : @"NO"); | |
| 331 | + | |
| 332 | + if (!_activitySuccess) return; | |
| 333 | + _enterBackground = NO; | |
| 334 | + | |
| 335 | + /** | |
| 336 | + * 视讯云平台:APP返回前台重新将活动状态改为“1”,若失败将推流状态改为error,并通知开发者 | |
| 337 | + */ | |
| 338 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 339 | +// [self requestStartStreamer:NULL failure:^(NSDictionary *errorDic) { | |
| 340 | +// | |
| 341 | +// _streamErrorCode = CNLiveStreamErrorCode_ABNORMAL; | |
| 342 | +// _streamState = CNLiveStreamStateError; | |
| 343 | +// | |
| 344 | +// [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamStateDidChangeNotification | |
| 345 | +// object:nil | |
| 346 | +// userInfo:nil]; | |
| 347 | +// } timeString:timeString]; | |
| 348 | + }]; | |
| 349 | +} | |
| 350 | + | |
| 351 | + | |
| 352 | +- (void)quitStreaming { | |
| 353 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 354 | + name:KSYCaptureStateDidChangeNotification | |
| 355 | + object:nil]; | |
| 356 | + | |
| 357 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 358 | + name:KSYStreamStateDidChangeNotification | |
| 359 | + object:nil]; | |
| 360 | + | |
| 361 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 362 | + name:UIApplicationWillResignActiveNotification | |
| 363 | + object:nil]; | |
| 364 | + | |
| 365 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 366 | + name:UIApplicationDidBecomeActiveNotification | |
| 367 | + object:nil]; | |
| 368 | + | |
| 369 | + | |
| 370 | + [self.heartTimer invalidate]; | |
| 371 | + self.heartTimer = nil; | |
| 372 | + | |
| 373 | + [_manager destroyStreamerManager]; | |
| 374 | + _manager = nil; | |
| 375 | + | |
| 376 | +} | |
| 377 | + | |
| 378 | +#pragma mark - capture | |
| 379 | +//采集分辨率 | |
| 380 | +- (void)setCapPreset:(NSString *)capPreset { | |
| 381 | + _manager.kit.capPreset = capPreset; | |
| 382 | + _capPreset = capPreset; | |
| 383 | +} | |
| 384 | + | |
| 385 | +//查询实际的采集分辨率 | |
| 386 | +- (CGSize)captureDimension { | |
| 387 | + return [_manager.kit captureDimension]; | |
| 388 | +} | |
| 389 | + | |
| 390 | +//预览分辨率 | |
| 391 | +- (void)setPreviewDimension:(CGSize)previewDimension { | |
| 392 | + _manager.kit.previewDimension = previewDimension; | |
| 393 | + _previewDimension = previewDimension; | |
| 394 | +} | |
| 395 | + | |
| 396 | +//推流分辨率 | |
| 397 | +- (void)setStreamDimension:(CGSize)streamDimension { | |
| 398 | + _manager.kit.streamDimension = streamDimension; | |
| 399 | + _streamDimension = streamDimension; | |
| 400 | +} | |
| 401 | + | |
| 402 | +//视频帧率 | |
| 403 | +- (void)setVideoFPS:(int)videoFPS { | |
| 404 | + _manager.kit.videoFPS = videoFPS; | |
| 405 | + _videoFPS = videoFPS; | |
| 406 | +} | |
| 407 | + | |
| 408 | +//摄像头位置 | |
| 409 | +- (void)setCameraPosition:(AVCaptureDevicePosition)cameraPosition { | |
| 410 | + _manager.kit.cameraPosition = cameraPosition; | |
| 411 | + _cameraPosition = cameraPosition; | |
| 412 | +} | |
| 413 | + | |
| 414 | +//摄像头朝向 | |
| 415 | +-(void)setVideoOrientationBy:(UIInterfaceOrientation)uiOrien { | |
| 416 | + _manager.kit.videoOrientation = uiOrien; | |
| 417 | +} | |
| 418 | + | |
| 419 | +#pragma mark - | |
| 420 | +#pragma mark - mirror & rotate | |
| 421 | + | |
| 422 | +//预览设置成镜像模式 | |
| 423 | +- (void)setPreviewMirrored:(BOOL)previewMirrored { | |
| 424 | + _manager.kit.previewMirrored = previewMirrored; | |
| 425 | + _previewMirrored = previewMirrored; | |
| 426 | +} | |
| 427 | + | |
| 428 | +//推流设置成镜像模式 | |
| 429 | +- (void)setStreamerMirrored:(BOOL)streamerMirrored { | |
| 430 | + _manager.kit.streamerMirrored = streamerMirrored; | |
| 431 | + _streamerMirrored = streamerMirrored; | |
| 432 | +} | |
| 433 | + | |
| 434 | +//旋转预览视图 | |
| 435 | +- (void)rotatePreviewTo:(UIInterfaceOrientation)orie | |
| 436 | +{ | |
| 437 | + [_manager.kit rotatePreviewTo:orie]; | |
| 438 | +} | |
| 439 | + | |
| 440 | +//旋转推流画面 | |
| 441 | +- (void)rotateStreamTo:(UIInterfaceOrientation)orie { | |
| 442 | + [_manager.kit rotateStreamTo:orie]; | |
| 443 | +} | |
| 444 | + | |
| 445 | +#pragma mark - | |
| 446 | +#pragma mark - camera operation | |
| 447 | + | |
| 448 | +- (BOOL)switchCamera { | |
| 449 | + return [_manager.kit switchCamera]; | |
| 450 | +} | |
| 451 | + | |
| 452 | +- (BOOL)isTorchSupported { | |
| 453 | + return [_manager.kit isTorchSupported]; | |
| 454 | +} | |
| 455 | + | |
| 456 | +- (AVCaptureDevice *)getCurrentCameraDevices { | |
| 457 | + return [_manager.kit getCurrentCameraDevices]; | |
| 458 | +} | |
| 459 | + | |
| 460 | +//设置闪光灯 | |
| 461 | +- (void)setTorchMode:(AVCaptureTorchMode)mode { | |
| 462 | + [_manager.kit setTorchMode:mode]; | |
| 463 | +} | |
| 464 | + | |
| 465 | +//闪光灯切换 | |
| 466 | +- (void)toggleTorch { | |
| 467 | + [_manager.kit toggleTorch]; | |
| 468 | +} | |
| 469 | + | |
| 470 | +#pragma mark - | |
| 471 | +#pragma mark - reconnect(由于开启会暴露推流地址,暂不开放) | |
| 472 | +@synthesize maxAutoRetry = _maxAutoRetry; | |
| 473 | +- (void)setMaxAutoRetry:(int)maxAutoRetry { | |
| 474 | + if (_isCNLiveStreaming) { | |
| 475 | + _maxAutoRetry = MAX(0, maxAutoRetry); | |
| 476 | + _autoRetryCnt = _maxAutoRetry; | |
| 477 | + } else { | |
| 478 | + _manager.kit.maxAutoRetry = maxAutoRetry; | |
| 479 | + } | |
| 480 | +} | |
| 481 | + | |
| 482 | +@synthesize autoRetryDelay = _autoRetryDelay; | |
| 483 | +- (void)setAutoRetryDelay:(double)autoRetryDelay { | |
| 484 | + if (_isCNLiveStreaming) { | |
| 485 | + _autoRetryDelay = MAX(0.1, autoRetryDelay); | |
| 486 | + _manager.kit.autoRetryDelay = autoRetryDelay; | |
| 487 | + } else { | |
| 488 | + _manager.kit.autoRetryDelay = autoRetryDelay; | |
| 489 | + } | |
| 490 | +} | |
| 491 | + | |
| 492 | +- (void)tryReconnect { | |
| 493 | + | |
| 494 | + // NSLog(@"tryReconnect---->autoRetryCnt:%d----initiativeRetry:%@----reteying:%@----retryFailed:%@", _autoRetryCnt, _initiativeRetry ? @"YES" : @"NO", _reteying?@"YES":@"NO", _retryFailed?@"YES":@"NO"); | |
| 495 | + | |
| 496 | + | |
| 497 | + if (_autoRetryCnt < 1) { | |
| 498 | + return; | |
| 499 | + } | |
| 500 | + | |
| 501 | + if (_reteying == YES) { | |
| 502 | + return; | |
| 503 | + } | |
| 504 | + | |
| 505 | + _reteying = YES; | |
| 506 | + | |
| 507 | + // NSLog(@"tryReconnect---->autoRetryCnt:%d----initiativeRetry:%@----reteying:%@----retryFailed:%@", _autoRetryCnt, _initiativeRetry ? @"YES" : @"NO", _reteying?@"YES":@"NO", _retryFailed?@"YES":@"NO"); | |
| 508 | + | |
| 509 | + | |
| 510 | + if (_autoRetryCnt == _maxAutoRetry) { | |
| 511 | + _reconnectStatus = CNLiveReconnectStatusStart; | |
| 512 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamReconnectStatusChangedNotification | |
| 513 | + object:nil | |
| 514 | + userInfo:nil]; | |
| 515 | + } | |
| 516 | + | |
| 517 | + int64_t delaySec = (int64_t)(_autoRetryDelay * NSEC_PER_SEC); | |
| 518 | + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, delaySec); | |
| 519 | + dispatch_after(delay, dispatch_get_main_queue(), ^{ | |
| 520 | + | |
| 521 | + if (!_manager.kit.streamerBase.isStreaming) { | |
| 522 | + // if (!_streamerBase.isStreaming) { | |
| 523 | + | |
| 524 | + if (_logPrintEnable) { | |
| 525 | + NSLog(@"CNLiveGPUStreamerKit-->第(%d)次重连/(%d)", (_maxAutoRetry-_autoRetryCnt+1),_maxAutoRetry); | |
| 526 | + } | |
| 527 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 528 | +// [self requestStartStreamer:NULL failure:NULL timeString:timeString]; | |
| 529 | + }]; | |
| 530 | + | |
| 531 | + _autoRetryCnt--; | |
| 532 | + if (_autoRetryCnt == 0) { | |
| 533 | + _retryFailed = YES; | |
| 534 | + } | |
| 535 | + } | |
| 536 | + }); | |
| 537 | +} | |
| 538 | + | |
| 539 | +- (void)recoverReconnectConfig { | |
| 540 | + _retryFailed = NO; | |
| 541 | +} | |
| 542 | + | |
| 543 | +#pragma mark - | |
| 544 | +#pragma mark - KSYMethod | |
| 545 | +- (UIView *)preview { | |
| 546 | + return (UIView *)_manager.kit.preview; | |
| 547 | +} | |
| 548 | + | |
| 549 | +//启动预览 | |
| 550 | +- (void)startPreview:(UIView *)view { | |
| 551 | + [_manager.kit startPreview:view]; | |
| 552 | +} | |
| 553 | + | |
| 554 | +//关闭预览 | |
| 555 | +- (void)stopPreview { | |
| 556 | + [_manager.kit stopPreview]; | |
| 557 | +} | |
| 558 | + | |
| 559 | +//开启推流(鉴权) | |
| 560 | +- (void)startStream:(void (^)(void))successBlock failure:(void (^)(NSDictionary *))failureBlock { | |
| 561 | + //若活动创建成功,直接开启活动 | |
| 562 | + if (_activitySuccess) { | |
| 563 | + /** | |
| 564 | + * 手动重新开始推流,将重连次数还原;手动重连:YES | |
| 565 | + */ | |
| 566 | + _autoRetryCnt = _maxAutoRetry; | |
| 567 | + _initiativeRetry = YES; | |
| 568 | + _reteying = NO; | |
| 569 | + _retryFailed = NO; | |
| 570 | + | |
| 571 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 572 | +// [self requestStartStreamer:successBlock failure:failureBlock timeString:timeString]; | |
| 573 | + }]; | |
| 574 | + | |
| 575 | + } else { | |
| 576 | + | |
| 577 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 578 | +// [self createActivityWithSuccess:successBlock failure:failureBlock timeString:timeString]; | |
| 579 | + }]; | |
| 580 | + | |
| 581 | + } | |
| 582 | +} | |
| 583 | + | |
| 584 | +- (void)startStreamWithUrl:(NSString *)url activityId:(NSString *)activityId success:(void (^)(void))successBlock failure:(void (^)(NSDictionary *))failureBlock { | |
| 585 | + _activitySuccess = YES; //后台已创建活动,_activitySuccess为YES | |
| 586 | + /** | |
| 587 | + * 手动重新开始推流,将重连次数还原;手动重连:YES | |
| 588 | + */ | |
| 589 | + _autoRetryCnt = _maxAutoRetry; | |
| 590 | + _initiativeRetry = YES; | |
| 591 | + _reteying = NO; | |
| 592 | + _retryFailed = NO; | |
| 593 | + | |
| 594 | + _activityId = activityId; //开发者传入的活动ID | |
| 595 | + _hostURL = [NSURL URLWithString:url]; //推流地址 | |
| 596 | + | |
| 597 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 598 | +// [self requestStartStreamer:successBlock failure:failureBlock timeString:timeString]; | |
| 599 | + }]; | |
| 600 | +} | |
| 601 | + | |
| 602 | +- (void)pauseStreaming { | |
| 603 | + [_manager.kit.streamerBase stopStream]; | |
| 604 | + _isRecording = NO; | |
| 605 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 606 | +// [self requestStopStreamerWithInfo:@"User Pause" timeString:timeString]; | |
| 607 | + }]; | |
| 608 | +} | |
| 609 | + | |
| 610 | +//开启推流(普通) | |
| 611 | +- (void)startStream:(NSURL *)hostURL { | |
| 612 | + [_manager.kit.streamerBase startStream:hostURL]; | |
| 613 | +} | |
| 614 | + | |
| 615 | +//停止推流 | |
| 616 | +- (void)stopStream { | |
| 617 | + if (_activitySuccess) { | |
| 618 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 619 | +// [self requestStopStreamerWithInfo:@"" timeString:timeString]; | |
| 620 | + }]; | |
| 621 | + _isRecording = NO; | |
| 622 | + [self.socket disconnect]; | |
| 623 | + | |
| 624 | + //断开RM连接 | |
| 625 | + [self quitRM]; | |
| 626 | + } | |
| 627 | + | |
| 628 | + [_manager.kit.streamerBase stopStream]; | |
| 629 | +} | |
| 630 | + | |
| 631 | +- (void)startRecordWithFilePath:(NSString *)path { | |
| 632 | + [_manager.kit.streamerBase startStream:[NSURL URLWithString:path]]; | |
| 633 | +} | |
| 634 | + | |
| 635 | +- (void)stopRecord { | |
| 636 | + [_manager.kit.streamerBase stopStream]; | |
| 637 | +} | |
| 638 | + | |
| 639 | +//进入后台 | |
| 640 | +- (void)appEnterBackground { | |
| 641 | + [_manager.kit appEnterBackground]; | |
| 642 | + | |
| 643 | + // //进入后台停止采集,避免占用资源 | |
| 644 | + // [_manager.kit.streamerBase stopStream]; | |
| 645 | +} | |
| 646 | + | |
| 647 | +//回到前台 | |
| 648 | +- (void)appBecomeActive { | |
| 649 | + [_manager.kit appBecomeActive]; | |
| 650 | +} | |
| 651 | + | |
| 652 | +//自动变焦 | |
| 653 | +- (void)focusAtPoint:(CGPoint)point { | |
| 654 | + [_manager.kit focusAtPoint:point]; | |
| 655 | +} | |
| 656 | + | |
| 657 | +//自动曝光 | |
| 658 | +- (void)exposureAtPoint:(CGPoint)point { | |
| 659 | + [_manager.kit exposureAtPoint:point]; | |
| 660 | +} | |
| 661 | + | |
| 662 | +//缩放 | |
| 663 | +- (void)setPinchZoomFactor:(CGFloat)pinchZoomFactor { | |
| 664 | + _manager.kit.pinchZoomFactor = pinchZoomFactor; | |
| 665 | +} | |
| 666 | + | |
| 667 | +- (CGFloat)pinchZoomFactor { | |
| 668 | + return _manager.kit.pinchZoomFactor; | |
| 669 | +} | |
| 670 | + | |
| 671 | +- (void)setStabilizationMode:(AVCaptureVideoStabilizationMode)stabilizationMode { | |
| 672 | + _manager.kit.stabilizationMode = stabilizationMode; | |
| 673 | +} | |
| 674 | + | |
| 675 | +#pragma mark - | |
| 676 | +#pragma mark - filters | |
| 677 | +- (void)setupFilter:(GPUImageOutput<GPUImageInput> *)filter { | |
| 678 | + [_manager.kit setupFilter:filter]; | |
| 679 | +} | |
| 680 | + | |
| 681 | +#pragma mark - | |
| 682 | +#pragma mark - 设置接口参数 | |
| 683 | +- (void)setActivityId:(NSString *)activityId { | |
| 684 | + _activityId = [NSString stringWithFormat:@"%@", activityId]; | |
| 685 | + [_parameter setObject:[NSString stringWithFormat:@"%@", activityId] forKey:@"activityId"]; | |
| 686 | +} | |
| 687 | + | |
| 688 | +- (void)setChannelId:(NSString *)channelId { | |
| 689 | + _contentId = channelId; | |
| 690 | + [_parameter setObject:[NSString stringWithFormat:@"%@", channelId] forKey:@"channelId"]; | |
| 691 | +} | |
| 692 | + | |
| 693 | +- (void)setIsHorizontalScreen:(BOOL)isHorizontalScreen { | |
| 694 | + _isHorizontalScreen = isHorizontalScreen; | |
| 695 | + [_parameter setObject:isHorizontalScreen ? @"1" :@"0" forKey:@"isHorizontalScreen"]; | |
| 696 | +} | |
| 697 | + | |
| 698 | +- (void)setActivityName:(NSString *)activityName { | |
| 699 | + _activityName = activityName; | |
| 700 | + [_parameter setObject:[NSString stringWithFormat:@"%@", activityName] forKey:@"activityName"]; | |
| 701 | +} | |
| 702 | + | |
| 703 | +- (void)setActivityStatus:(NSString *)activityStatus { | |
| 704 | + _activityStatus = activityStatus; | |
| 705 | + [_parameter setObject:[NSString stringWithFormat:@"%@", activityStatus] forKey:@"activityStatus"]; | |
| 706 | +} | |
| 707 | + | |
| 708 | +- (void)setStartTime:(NSString *)startTime { | |
| 709 | + _startTime = startTime; | |
| 710 | + [_parameter setObject:[NSString stringWithFormat:@"%@", startTime] forKey:@"startTime"]; | |
| 711 | +} | |
| 712 | + | |
| 713 | +- (void)setEndTime:(NSString *)endTime { | |
| 714 | + _endTime = endTime; | |
| 715 | + [_parameter setObject:[NSString stringWithFormat:@"%@", endTime] forKey:@"endTime"]; | |
| 716 | +} | |
| 717 | + | |
| 718 | +- (void)setCoverImgUrl:(NSString *)coverImgUrl { | |
| 719 | + _coverImgUrl = coverImgUrl; | |
| 720 | + [_parameter setObject:[NSString stringWithFormat:@"%@", coverImgUrl] forKey:@"coverImgUrl"]; | |
| 721 | +} | |
| 722 | + | |
| 723 | +- (void)setActivityCategory:(NSString *)activityCategory { | |
| 724 | + _activityCategory = activityCategory; | |
| 725 | + [_parameter setObject:[NSString stringWithFormat:@"%@", activityCategory] forKey:@"activityCategory"]; | |
| 726 | +} | |
| 727 | + | |
| 728 | +- (void)setExtensions:(NSString *)extensions { | |
| 729 | + _extensions = extensions; | |
| 730 | + [_parameter setObject:[NSString stringWithFormat:@"%@", extensions] forKey:@"extensions"]; | |
| 731 | +} | |
| 732 | + | |
| 733 | +- (void)setCallbackAcitvityStatushttpPostUrl:(NSString *)callbackAcitvityStatushttpPostUrl { | |
| 734 | + _callbackAcitvityStatushttpPostUrl = callbackAcitvityStatushttpPostUrl; | |
| 735 | + [_parameter setObject:[NSString stringWithFormat:@"%@", callbackAcitvityStatushttpPostUrl] forKey:@"callbackAcitvityStatushttpPostUrl"]; | |
| 736 | +} | |
| 737 | + | |
| 738 | +#pragma mark - GCDAsyncSocketDelegate | |
| 739 | + | |
| 740 | +- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port { | |
| 741 | + self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES]; | |
| 742 | + [self checkLongConnectByServe]; | |
| 743 | + // NSLog(@"socket -------- 已连接"); | |
| 744 | +} | |
| 745 | + | |
| 746 | +- (void)checkLongConnectByServe { | |
| 747 | + NSString *longConnect; | |
| 748 | + if (PLAYER_TestEnvironment) { | |
| 749 | + longConnect = [NSString stringWithFormat:@"test.%@#%@#",PLAYER_AppID,_activityId]; | |
| 750 | + } else { | |
| 751 | + longConnect = [NSString stringWithFormat:@"%@#%@#",PLAYER_AppID,_activityId]; | |
| 752 | + } | |
| 753 | + NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding]; | |
| 754 | + // NSLog(@"socket-socket--socket---%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); | |
| 755 | + [self.socket writeData:data withTimeout:-1 tag:1]; | |
| 756 | +} | |
| 757 | + | |
| 758 | +- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag { | |
| 759 | + // NSLog(@"socket:%p didWriteDataWithTag:%ld", sock, tag); | |
| 760 | +} | |
| 761 | + | |
| 762 | +- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { | |
| 763 | + // NSLog(@"socket:%p didReadData:withTag:%ld", sock, tag); | |
| 764 | + | |
| 765 | + // NSString *httpResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | |
| 766 | + | |
| 767 | + // NSLog(@"HTTP Response:\n%@", httpResponse); | |
| 768 | + | |
| 769 | +} | |
| 770 | + | |
| 771 | +- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err { | |
| 772 | + // NSLog(@"socket-------- 断开连接 %@", err); | |
| 773 | + | |
| 774 | + if (_isRecording) { | |
| 775 | + | |
| 776 | + _isRecording = NO; | |
| 777 | + | |
| 778 | + if (self.logPrintEnable) { | |
| 779 | + NSLog(@"CNLiveGPUStreamerKit-->由于监听心跳未能正常运作而暂时断开推流,reason:%@",err?err.localizedDescription:@"nil"); | |
| 780 | + } | |
| 781 | + | |
| 782 | + [_manager.kit.streamerBase stopStream]; | |
| 783 | + | |
| 784 | + //如果socket异常断开,发送异常通知 | |
| 785 | + _streamErrorCode = CNLiveStreamErrorCode_ABNORMAL; | |
| 786 | + _streamState = CNLiveStreamStateError; | |
| 787 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamStateDidChangeNotification | |
| 788 | + object:nil | |
| 789 | + userInfo:nil]; | |
| 790 | + | |
| 791 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 792 | +// [self requestStopStreamerWithInfo:@"CNLiveStreamErrorCode_ABNORMAL_1" timeString:timeString]; | |
| 793 | + }]; | |
| 794 | + | |
| 795 | + | |
| 796 | + //重连失败通知开发者 | |
| 797 | + if (_retryFailed && _isCNLiveStreaming) { | |
| 798 | + _reconnectStatus = CNLiveReconnectStatusFailed; | |
| 799 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveStreamReconnectStatusChangedNotification | |
| 800 | + object:nil | |
| 801 | + userInfo:nil]; | |
| 802 | + [self recoverReconnectConfig]; | |
| 803 | + } | |
| 804 | + | |
| 805 | + // NSLog(@"socketBreak---->autoRetryCnt:%d----initiativeRetry:%@----reteying:%@----retryFailed:%@", _autoRetryCnt, _initiativeRetry ? @"YES" : @"NO", _reteying?@"YES":@"NO", _retryFailed?@"YES":@"NO"); | |
| 806 | + | |
| 807 | + if (_autoRetryCnt>0 && !_initiativeRetry) { | |
| 808 | + [self tryReconnect]; | |
| 809 | + //NSLog(@"CNLiveStreamerKit-->reconnect ----- socketERROR"); | |
| 810 | + } | |
| 811 | + | |
| 812 | + } | |
| 813 | + | |
| 814 | + [self.heartTimer invalidate]; | |
| 815 | + self.heartTimer = nil; | |
| 816 | +} | |
| 817 | + | |
| 818 | +#pragma mark - RM | |
| 819 | +- (void)initRCloudWithAppId:(NSString *)appId appKey:(NSString *)appKey connectResult:(void(^)(BOOL result))result { | |
| 820 | + [[CNLiveMsgManager sharedCNLiveMsgManager] initCNLiveMsgToolsWithAppKey:appKey appId:appId]; | |
| 821 | + | |
| 822 | + //连接RM服务器 | |
| 823 | + [[CNLiveMsgManager sharedCNLiveMsgManager] justOnLineSuccess:^(NSString *toolsId) { | |
| 824 | + | |
| 825 | + //加入聊天室 | |
| 826 | + [self joinRoomResult:result]; | |
| 827 | + | |
| 828 | + } error:^(NSInteger errors) { | |
| 829 | + | |
| 830 | + if (result) { | |
| 831 | + result(NO); | |
| 832 | + } | |
| 833 | + | |
| 834 | + }]; | |
| 835 | + | |
| 836 | +} | |
| 837 | + | |
| 838 | +//加入聊天室 | |
| 839 | +- (void)joinRoomResult:(void(^)(BOOL result))result { | |
| 840 | + //注册通知 | |
| 841 | + [self initRCloudNotificationObservers]; | |
| 842 | + | |
| 843 | + NSString *contentId = _contentId; | |
| 844 | + if (![_contentId containsString:@"_"]) { | |
| 845 | + NSString *spId = [[PLAYER_AppID componentsSeparatedByString:@"_"] firstObject]; | |
| 846 | + contentId = [NSString stringWithFormat:@"%@_%@", spId, _contentId]; | |
| 847 | + } | |
| 848 | + | |
| 849 | + _roomId = [NSString stringWithFormat:@"CNLiveAnchorStatus_%@",contentId]; | |
| 850 | + | |
| 851 | + // NSLog(@"**********************----roomid %@",_roomId); | |
| 852 | + | |
| 853 | + //加入聊天室 | |
| 854 | + [[CNLiveMsgManager sharedCNLiveMsgManager] gatherIfNullCreate:_roomId | |
| 855 | + messageCount:1 | |
| 856 | + success:^(NSString *targetId) { | |
| 857 | + | |
| 858 | + if (result) { | |
| 859 | + result(YES); | |
| 860 | + } | |
| 861 | + | |
| 862 | + } error:^(NSInteger errors, NSString *targetId) { | |
| 863 | + | |
| 864 | + // NSLog(@"***********--加入聊天室失败 ----errors %ld",(long)errors); | |
| 865 | + | |
| 866 | + if (result) { | |
| 867 | + result(NO); | |
| 868 | + } | |
| 869 | + }]; | |
| 870 | +} | |
| 871 | + | |
| 872 | +- (void)initRCloudNotificationObservers { | |
| 873 | + //如果已经注册通知直接返回,无需重复注册 | |
| 874 | + if (_registerChatNoti) return; | |
| 875 | + | |
| 876 | + //接收消息通知 | |
| 877 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:MsgToolsStreamReceiveNotification object:nil]; | |
| 878 | + | |
| 879 | + _registerChatNoti = YES; | |
| 880 | + | |
| 881 | +} | |
| 882 | + | |
| 883 | +- (void)receiveMessage:(NSNotification *)noti { | |
| 884 | + | |
| 885 | + if (!noti.userInfo[@"message"]) return; | |
| 886 | + | |
| 887 | + int newValue = [noti.userInfo[@"message"] intValue]; | |
| 888 | + | |
| 889 | + if (_originalStatus == newValue) return; //如果新收到的状态与上一次状态一样不作处理 | |
| 890 | + if (!_originalStatus && newValue == 2) { | |
| 891 | + return; | |
| 892 | + } | |
| 893 | + | |
| 894 | + _originalStatus = newValue; | |
| 895 | + | |
| 896 | + if (newValue == 2) { | |
| 897 | + //发送通知 | |
| 898 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 899 | + [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveServiceConnectBreakNotification object:nil userInfo:nil]; | |
| 900 | + }); | |
| 901 | + } | |
| 902 | + | |
| 903 | +} | |
| 904 | + | |
| 905 | +- (void)quitRM { | |
| 906 | + if (!_registerChatNoti) return; | |
| 907 | + | |
| 908 | + _registerChatNoti = NO; | |
| 909 | + | |
| 910 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 911 | + name:MsgToolsStreamReceiveNotification | |
| 912 | + object:nil]; | |
| 913 | + | |
| 914 | + if ([CNLiveStreamerTools isEmptyString:_roomId]) { | |
| 915 | + return; | |
| 916 | + } | |
| 917 | + | |
| 918 | + [[CNLiveMsgManager sharedCNLiveMsgManager] disperse:_roomId success:^(NSString *targetId) { | |
| 919 | + | |
| 920 | + } error:^(NSInteger errors, NSString *targetId) { | |
| 921 | + | |
| 922 | + }]; | |
| 923 | + | |
| 924 | +} | |
| 925 | + | |
| 926 | +#pragma mark - getter | |
| 927 | +- (CNLiveWatermark *)watermark { | |
| 928 | + if (!_watermark) { | |
| 929 | + _watermark = [[CNLiveWatermark alloc] init]; | |
| 930 | + } | |
| 931 | + return _watermark; | |
| 932 | +} | |
| 933 | + | |
| 934 | +- (CNLiveStreamerBase *)streamerBase { | |
| 935 | + if (!_streamerBase) { | |
| 936 | + _streamerBase = [[CNLiveStreamerBase alloc] init]; | |
| 937 | + } | |
| 938 | + return _streamerBase; | |
| 939 | +} | |
| 940 | + | |
| 941 | +- (CNLiveAudioCapture *)audioCapture { | |
| 942 | + if (!_audioCapture) { | |
| 943 | + _audioCapture = [[CNLiveAudioCapture alloc] init]; | |
| 944 | + } | |
| 945 | + return _audioCapture; | |
| 946 | +} | |
| 947 | + | |
| 948 | +- (CNLiveBeautifyFaceFilter *)beautifyFaceFilter { | |
| 949 | + if (!_beautifyFaceFilter) { | |
| 950 | + _beautifyFaceFilter = [[CNLiveBeautifyFaceFilter alloc] init]; | |
| 951 | + } | |
| 952 | + return _beautifyFaceFilter; | |
| 953 | +} | |
| 954 | + | |
| 955 | +- (CNLiveBgmPlayer *)bgmPlayer { | |
| 956 | + if (!_bgmPlayer) { | |
| 957 | + _bgmPlayer = [[CNLiveBgmPlayer alloc] init]; | |
| 958 | + } | |
| 959 | + return _bgmPlayer; | |
| 960 | +} | |
| 961 | + | |
| 962 | +#pragma mark - dealloc | |
| 963 | +- (void)dealloc { | |
| 964 | + | |
| 965 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 966 | + name:KSYCaptureStateDidChangeNotification | |
| 967 | + object:nil]; | |
| 968 | + | |
| 969 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 970 | + name:KSYStreamStateDidChangeNotification | |
| 971 | + object:nil]; | |
| 972 | + | |
| 973 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 974 | + name:UIApplicationDidEnterBackgroundNotification | |
| 975 | + object:nil]; | |
| 976 | + | |
| 977 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 978 | + name:UIApplicationWillEnterForegroundNotification | |
| 979 | + object:nil]; | |
| 980 | + | |
| 981 | + [[NSNotificationCenter defaultCenter] removeObserver:self | |
| 982 | + name:MsgToolsStreamReceiveNotification | |
| 983 | + object:nil]; | |
| 984 | + | |
| 985 | + [UIApplication sharedApplication].idleTimerDisabled = NO; | |
| 986 | + | |
| 987 | + | |
| 988 | + [_manager destroyStreamerManager]; | |
| 989 | + _manager = nil; | |
| 990 | +} | |
| 991 | + | |
| 992 | +- (UIView *)floatingWindowFrame:(CGRect)rect { | |
| 993 | + KSYGPUView *gpuView = [[KSYGPUView alloc] init]; | |
| 994 | + gpuView.frame = CGRectMake(0, 0, rect.size.width, rect.size.height); | |
| 995 | + [_manager.kit.vPreviewMixer addTarget:gpuView]; | |
| 996 | + UIView *view = [[UIView alloc] initWithFrame:rect]; | |
| 997 | + [view addSubview:gpuView]; | |
| 998 | + return view; | |
| 999 | +} | |
| 1000 | + | |
| 1001 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerManager.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerManager.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <libksygpulive/KSYGPUStreamerKit.h> | |
| 11 | + | |
| 12 | +NS_ASSUME_NONNULL_BEGIN | |
| 13 | + | |
| 14 | +#define STREAMER_Manager [CNLiveStreamerManager manager].kit | |
| 15 | + | |
| 16 | +@interface CNLiveStreamerManager : NSObject | |
| 17 | +@property (nonatomic, strong) KSYGPUStreamerKit *kit; | |
| 18 | + | |
| 19 | ++ (CNLiveStreamerManager *)manager; | |
| 20 | + | |
| 21 | +- (void)destroyStreamerManager; | |
| 22 | + | |
| 23 | +@end | |
| 24 | + | |
| 25 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerManager.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerManager.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveStreamerManager.h" | |
| 10 | + | |
| 11 | +@implementation CNLiveStreamerManager | |
| 12 | + | |
| 13 | +static dispatch_once_t onceToken; | |
| 14 | +static CNLiveStreamerManager * _instance = nil; | |
| 15 | + | |
| 16 | ++ (CNLiveStreamerManager *)manager { | |
| 17 | + dispatch_once(&onceToken, ^{ | |
| 18 | + _instance = [[self alloc] init]; | |
| 19 | + | |
| 20 | + }); | |
| 21 | + | |
| 22 | + return _instance; | |
| 23 | +} | |
| 24 | + | |
| 25 | +- (void)destroyStreamerManager { | |
| 26 | + if (onceToken) { | |
| 27 | + onceToken = 0; | |
| 28 | + } | |
| 29 | + _instance = nil; | |
| 30 | +} | |
| 31 | + | |
| 32 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerTools.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerTools.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | + | |
| 11 | +NS_ASSUME_NONNULL_BEGIN | |
| 12 | + | |
| 13 | +@interface CNLiveStreamerTools : NSObject | |
| 14 | +//SHA加密 | |
| 15 | ++ (NSMutableDictionary *)encryptionSignWithParameter:(NSMutableDictionary *)parameter; | |
| 16 | + | |
| 17 | ++ (NSString*)sha1:(NSString *)string; | |
| 18 | + | |
| 19 | ++ (NSString *)signvalue:(NSDictionary*)parameter; | |
| 20 | + | |
| 21 | +//探针 | |
| 22 | ++ (void)statSDKWithDate:(NSDate *)date activityId:(NSString *)activityId; | |
| 23 | + | |
| 24 | +//错误探针 | |
| 25 | ++ (void)errorStat:(NSString *)errorString activityId:(NSString *)activityId; | |
| 26 | + | |
| 27 | +//获取uuid | |
| 28 | ++ (NSString *)getUidForStat:(NSDate *)date; | |
| 29 | + | |
| 30 | ++ (NSString *)uidForStat:(NSString *)date; | |
| 31 | + | |
| 32 | +//错误信息 | |
| 33 | ++ (NSError *)customErrorWithResponseObject:(id)responseObject errorInfo:(NSString *)errorInfo; | |
| 34 | + | |
| 35 | +//验证是是否为是空字符串 | |
| 36 | ++ (BOOL)isEmptyString:(NSString *)string; | |
| 37 | + | |
| 38 | +//获取网络真时时间 | |
| 39 | ++ (void)getRealTimeString:(void(^)(NSString *timeString))realTime; | |
| 40 | + | |
| 41 | +@end | |
| 42 | + | |
| 43 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamerTools.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerTools.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveStreamerTools.h" | |
| 10 | +#import "CommonCrypto/CommonDigest.h" | |
| 11 | +#import <UIKit/UIKit.h> | |
| 12 | +#import "CNLivePlayerManager.h" | |
| 13 | +#import "CNLivePlayerUrl.h" | |
| 14 | + | |
| 15 | +@implementation CNLiveStreamerTools | |
| 16 | + | |
| 17 | ++ (NSMutableDictionary *)encryptionSignWithParameter:(NSMutableDictionary *)parameter { | |
| 18 | + NSString *string = [NSString stringWithFormat:@"%@&key=%@",[self signvalue:parameter],PLAYER_AppKey]; | |
| 19 | + NSString *signString = [[self sha1:string] uppercaseString]; | |
| 20 | + NSMutableDictionary *startPara = [NSMutableDictionary dictionaryWithDictionary:parameter]; | |
| 21 | + [startPara setObject:signString forKey:@"sign"]; | |
| 22 | + return startPara; | |
| 23 | +} | |
| 24 | + | |
| 25 | +#pragma mark - | |
| 26 | +#pragma mark - 加密 | |
| 27 | ++ (NSString*)sha1:(NSString *)string | |
| 28 | +{ | |
| 29 | + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; | |
| 30 | + | |
| 31 | + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; | |
| 32 | + | |
| 33 | + CC_SHA1(data.bytes, data.length, digest); | |
| 34 | + | |
| 35 | + NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; | |
| 36 | + | |
| 37 | + for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) | |
| 38 | + [output appendFormat:@"%02x", digest[i]]; | |
| 39 | + | |
| 40 | + while ([[output substringToIndex:1] isEqualToString:@"0"]) { | |
| 41 | + output = [[NSMutableString alloc] initWithString:[output substringFromIndex:1]]; | |
| 42 | + } | |
| 43 | + | |
| 44 | + return output; | |
| 45 | +} | |
| 46 | + | |
| 47 | ++ (NSString *)signvalue:(NSDictionary*)parameter | |
| 48 | +{ | |
| 49 | + //对所有传入参数按照字段名的 ASCII 码从小到大排序 | |
| 50 | + NSArray *keyArr=[parameter allKeys]; | |
| 51 | + NSArray *arr = [keyArr sortedArrayUsingSelector:@selector(compare:)]; | |
| 52 | + | |
| 53 | + NSMutableString *string1 = [[NSMutableString alloc]init]; | |
| 54 | + for (int i=0; i<arr.count; i++) { | |
| 55 | + | |
| 56 | + NSString *parameterString = parameter[[arr objectAtIndex:i]]; | |
| 57 | + if (parameterString.length > 0) { | |
| 58 | + [string1 appendString:[NSString stringWithFormat:@"%@=%@&",[arr objectAtIndex:i],parameter[[arr objectAtIndex:i]]]]; | |
| 59 | + } | |
| 60 | + } | |
| 61 | + | |
| 62 | + if (string1.length > 0) { | |
| 63 | + [string1 deleteCharactersInRange:NSMakeRange(string1.length-1, 1)]; | |
| 64 | + } | |
| 65 | + | |
| 66 | + return string1; | |
| 67 | +} | |
| 68 | + | |
| 69 | +#pragma mark - | |
| 70 | +#pragma mark - 探针 | |
| 71 | ++ (void)statSDKWithDate:(NSDate *)date activityId:(NSString *)activityId | |
| 72 | +{ | |
| 73 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 74 | + NSString *stamp = [NSString stringWithFormat:@"%.3f", [timeString floatValue]]; | |
| 75 | + stamp = [stamp stringByReplacingOccurrencesOfString:@"." withString:@""]; | |
| 76 | + NSString *vvUrl = [NSString stringWithFormat:@"%@?sver=%@&uid=%@&tm=%@&from=apple&spid=&appid=%@&version=1003_%@&eventid=1600&uri=&sid=&plat=i_%@&objid=%@_aid", StatUrl, StatVersion, [self uidForStat:stamp], stamp, PLAYER_AppID, PlayerSDKVersion, StatAppVersion,activityId]; | |
| 77 | + [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:vvUrl]] delegate:nil]; | |
| 78 | + }]; | |
| 79 | + | |
| 80 | +} | |
| 81 | + | |
| 82 | ++ (void)errorStat:(NSString *)errorString activityId:(NSString *)activityId | |
| 83 | +{ | |
| 84 | + __block NSString *errorStr; | |
| 85 | + [CNLiveStreamerTools getRealTimeString:^(NSString *timeString) { | |
| 86 | + errorStr = [self urlEncodeUsingEncodingString:errorString]; | |
| 87 | + NSDate *date = [NSDate date]; | |
| 88 | + NSString *stamp = [NSString stringWithFormat:@"%.3f", [date timeIntervalSince1970]]; | |
| 89 | + stamp = [stamp stringByReplacingOccurrencesOfString:@"." withString:@""]; | |
| 90 | + NSString *vvUrl = [NSString stringWithFormat:@"%@?sver=%@&uid=%@&tm=%@&from=apple&spid=&appid=%@&version=1003_%@&eventid=1000&uri=%@&sid=&plat=i_%@&objid=%@_aid", StatUrl, StatVersion, [self uidForStat:stamp], stamp, PLAYER_AppID, PlayerSDKVersion, errorStr, StatAppVersion,activityId]; | |
| 91 | + [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:vvUrl]] delegate:nil]; | |
| 92 | + | |
| 93 | + }]; | |
| 94 | +} | |
| 95 | + | |
| 96 | ++ (NSString *)urlEncodeUsingEncodingString:(NSString *)string { | |
| 97 | + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, | |
| 98 | + (__bridge CFStringRef)string, | |
| 99 | + NULL, | |
| 100 | + (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", | |
| 101 | + CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); | |
| 102 | +} | |
| 103 | + | |
| 104 | +#pragma mark - | |
| 105 | +#pragma mark - uuid | |
| 106 | ++ (NSString *)getUidForStat:(NSDate *)date { | |
| 107 | + NSString *uid = @""; | |
| 108 | + | |
| 109 | + NSUserDefaults *defauts = [NSUserDefaults standardUserDefaults]; | |
| 110 | + | |
| 111 | + if ([defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"]) { | |
| 112 | + uid = [[defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"] copy]; | |
| 113 | + return uid; | |
| 114 | + } | |
| 115 | + else { | |
| 116 | + | |
| 117 | + NSString *stamp = [NSString stringWithFormat:@"%.3f", [date timeIntervalSince1970]]; | |
| 118 | + stamp = [stamp stringByReplacingOccurrencesOfString:@"." withString:@""]; | |
| 119 | + | |
| 120 | + NSString *idfv = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] stringByReplacingOccurrencesOfString:@"-" withString:@""]; | |
| 121 | + uid = [NSString stringWithFormat:@"%@_%@", idfv, stamp]; | |
| 122 | + | |
| 123 | + [defauts setObject:uid forKey:@"kCNLiveUserDefaultsUIDKey"]; | |
| 124 | + [defauts synchronize]; | |
| 125 | + } | |
| 126 | + | |
| 127 | + return uid; | |
| 128 | + | |
| 129 | +} | |
| 130 | + | |
| 131 | ++ (NSString *)uidForStat:(NSString *)date { | |
| 132 | + | |
| 133 | + NSString *uid = @""; | |
| 134 | + | |
| 135 | + NSUserDefaults *defauts = [NSUserDefaults standardUserDefaults]; | |
| 136 | + | |
| 137 | + if ([defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"]) { | |
| 138 | + uid = [[defauts objectForKey:@"kCNLiveUserDefaultsUIDKey"] copy]; | |
| 139 | + return uid; | |
| 140 | + } else { | |
| 141 | + | |
| 142 | + // NSString *stamp = [NSString stringWithFormat:@"%.3f", [date timeIntervalSince1970]]; | |
| 143 | + // stamp = [stamp stringByReplacingOccurrencesOfString:@"." withString:@""]; | |
| 144 | + | |
| 145 | + NSString *idfv = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] stringByReplacingOccurrencesOfString:@"-" withString:@""]; | |
| 146 | + uid = [NSString stringWithFormat:@"%@_%@", idfv, date]; | |
| 147 | + | |
| 148 | + [defauts setObject:uid forKey:@"kCNLiveUserDefaultsUIDKey"]; | |
| 149 | + [defauts synchronize]; | |
| 150 | + } | |
| 151 | + | |
| 152 | + return uid; | |
| 153 | +} | |
| 154 | + | |
| 155 | +//错误信息 | |
| 156 | ++ (NSError *)customErrorWithResponseObject:(id)responseObject errorInfo:(NSString *) errorInfo | |
| 157 | +{ | |
| 158 | + //用于探针 | |
| 159 | + NSString *defaultErrorInfo; | |
| 160 | + NSInteger errorCode = -1; | |
| 161 | + if (!responseObject[@"errorMessage"] || [self isEmptyString:responseObject[@"errorMessage"]]) { | |
| 162 | + defaultErrorInfo = errorInfo; | |
| 163 | + } else { | |
| 164 | + defaultErrorInfo = responseObject[@"errorMessage"]; | |
| 165 | + errorCode = [responseObject[@"errorCode"] integerValue]; | |
| 166 | + } | |
| 167 | + | |
| 168 | + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey : defaultErrorInfo}]; | |
| 169 | + return error; | |
| 170 | +} | |
| 171 | + | |
| 172 | + | |
| 173 | ++ (BOOL)isEmptyString:(NSString *)string | |
| 174 | +{ | |
| 175 | + if (!string || ![string isKindOfClass:[NSString class]] || string.length == 0 || [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0) { | |
| 176 | + return YES; | |
| 177 | + } | |
| 178 | + return NO; | |
| 179 | +} | |
| 180 | + | |
| 181 | +#pragma mark - | |
| 182 | +#pragma mark - 真时时间 | |
| 183 | ++ (void)getRealTimeString:(void (^)(NSString * __nullable))realTime | |
| 184 | +{ | |
| 185 | + NSInteger time = [[NSDate date] timeIntervalSince1970]; | |
| 186 | + NSString *timeString = [NSString stringWithFormat:@"%ld", (long)time]; | |
| 187 | + realTime(timeString); | |
| 188 | + | |
| 189 | + // [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:HTTPorHTTPs@"api.cnlive.com/open/api2/timestamp"]] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {// https://m.baidu.com | |
| 190 | + // | |
| 191 | + // if (!response) { | |
| 192 | + // NSInteger time = [[NSDate date] timeIntervalSince1970]; | |
| 193 | + // NSString *timeString = [NSString stringWithFormat:@"%ld", (long)time]; | |
| 194 | + // dispatch_sync(dispatch_get_main_queue(), ^{ | |
| 195 | + // realTime(timeString); | |
| 196 | + // }); | |
| 197 | + // return ; | |
| 198 | + // } | |
| 199 | + // | |
| 200 | + // NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | |
| 201 | + // NSString *timestamp = [[httpResponse allHeaderFields] objectForKey:@"Date"]; | |
| 202 | + // | |
| 203 | + // NSString *timeStr = [timestamp substringWithRange:NSMakeRange(5, 20)]; | |
| 204 | + // NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; | |
| 205 | + // NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"GMT"]; | |
| 206 | + // NSLocale *local = [[NSLocale alloc]initWithLocaleIdentifier:@"en_US_POSIX"]; | |
| 207 | + // [formatter setLocale:local];//需要配置区域,不然会造成模拟器正常,真机日期为null的情况 | |
| 208 | + // [formatter setTimeZone:timeZone]; | |
| 209 | + // [formatter setDateFormat:@"dd MMM yyyy HH:mm:ss"];//设置源时间字符串的格式 | |
| 210 | + // NSDate *date = [formatter dateFromString:timeStr];//将源时间字符串转化为NSDate | |
| 211 | + // | |
| 212 | + // NSString *timeString = [NSString stringWithFormat:@"%.0f", [date timeIntervalSince1970]]; | |
| 213 | + // dispatch_sync(dispatch_get_main_queue(), ^{ | |
| 214 | + // realTime(timeString); | |
| 215 | + // }); | |
| 216 | + // }]; | |
| 217 | +} | |
| 218 | + | |
| 219 | +@end | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveWatermark.h
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveWatermark.h | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 水印 | |
| 11 | + */ | |
| 12 | + | |
| 13 | +#import <Foundation/Foundation.h> | |
| 14 | +#import <GPUImage/GPUImage.h> | |
| 15 | + | |
| 16 | +NS_ASSUME_NONNULL_BEGIN | |
| 17 | + | |
| 18 | +@interface CNLiveWatermark : NSObject | |
| 19 | +/** | |
| 20 | + @abstract 水印logo的图片 | |
| 21 | + @discussion 设置为nil为清除水印图片 | |
| 22 | + @discussion 请注意背景图片的尺寸, 太大的图片会导致内存占用过高 | |
| 23 | + @see | |
| 24 | + */ | |
| 25 | +@property (nonatomic, readwrite) GPUImagePicture *logoPic; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + 设置水印图片的朝向 | |
| 29 | + @param orien 图片的朝向 | |
| 30 | + */ | |
| 31 | +- (void)setLogoOrientaion:(UIImageOrientation) orien; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + @abstract 文字内容的图片 | |
| 35 | + @discussion 设置为nil为清除内容图片 | |
| 36 | + */ | |
| 37 | +@property (nonatomic, readwrite) GPUImagePicture *textPic; | |
| 38 | + | |
| 39 | +/** | |
| 40 | + @abstract 贴纸的图片 | |
| 41 | + @discussion 设置为nil为清除贴纸图片 | |
| 42 | + */ | |
| 43 | +@property (nonatomic, readwrite) GPUImageUIElement *aePic; | |
| 44 | + | |
| 45 | +/** | |
| 46 | + @abstract 水印logo的图片的位置和大小 | |
| 47 | + @discussion 位置和大小的单位为预览视图的百分比, 左上角为(0,0), 右下角为(1.0, 1.0) | |
| 48 | + @discussion 如果宽为0, 则根据图像的宽高比, 和设置的高度比例, 计算得到宽度的比例 | |
| 49 | + @discussion 如果高为0, 方法同上 | |
| 50 | + */ | |
| 51 | +@property (nonatomic, readwrite) CGRect logoRect; | |
| 52 | + | |
| 53 | +/** | |
| 54 | + @abstract 水印logo的图片的透明度 | |
| 55 | + @discussion alpha为透明度(0-1),0完全透明,1完全不透明 | |
| 56 | + */ | |
| 57 | +@property (nonatomic, readwrite) CGFloat logoAlpha; | |
| 58 | + | |
| 59 | +/** | |
| 60 | + @abstract 水印文字的label | |
| 61 | + @discussion 借用UILabel来指定文字的颜色,字体, 透明度, 对齐方式等属性 | |
| 62 | + @discussion 请注意保证背景图片的尺寸, 太大的图片会导致内存占用过高 | |
| 63 | + @warning 如果使用非等宽字体, 可能导致闪烁(默认为Courier) | |
| 64 | + @warning picMixer和UILabel都有alpha属性, 建议只选用其中一个, 固定另一个为1.0, | |
| 65 | + 为了减少接口, 建议直接使用UILabel的属性, | |
| 66 | + 如果两者同时使用, 最终图层的alpha为两者乘积 | |
| 67 | + */ | |
| 68 | +@property (nonatomic, readwrite) UILabel *textLabel; | |
| 69 | + | |
| 70 | +/** | |
| 71 | + @abstract 水印文字的位置和大小 | |
| 72 | + @discussion 位置和大小的单位为预览视图的百分比, 左上角为(0,0), 右下角为(1.0, 1.0) | |
| 73 | + @discussion 如果宽为0, 则根据文字图像的宽高比, 和设置的高度比例, 计算得到宽度的比例 | |
| 74 | + @discussion 如果高为0, 方法同上 | |
| 75 | + */ | |
| 76 | +@property (nonatomic, readwrite) CGRect textRect; | |
| 77 | + | |
| 78 | +/** | |
| 79 | + @abstract 刷新水印文字的内容 | |
| 80 | + @discussion 先修改文字的内容或格式,调用该方法后生效 | |
| 81 | + */ | |
| 82 | +- (void)updateTextLabel; | |
| 83 | + | |
| 84 | +@end | |
| 85 | + | |
| 86 | +NS_ASSUME_NONNULL_END | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveWatermark.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveWatermark.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveWatermark.h" | |
| 10 | +#import "CNLiveStreamerManager.h" | |
| 11 | + | |
| 12 | +@implementation CNLiveWatermark | |
| 13 | +- (void)setLogoPic:(GPUImagePicture *)logoPic { | |
| 14 | + _logoPic = logoPic; | |
| 15 | + STREAMER_Manager.logoPic = logoPic; | |
| 16 | +} | |
| 17 | + | |
| 18 | +- (void)setLogoOrientaion:(UIImageOrientation)orien { | |
| 19 | + [STREAMER_Manager setLogoOrientaion:orien]; | |
| 20 | +} | |
| 21 | + | |
| 22 | +- (void)setTextPic:(GPUImagePicture *)textPic { | |
| 23 | + _textPic = textPic; | |
| 24 | + STREAMER_Manager.textPic = textPic; | |
| 25 | +} | |
| 26 | + | |
| 27 | +- (void)setAePic:(GPUImageUIElement *)aePic { | |
| 28 | + _aePic = aePic; | |
| 29 | + STREAMER_Manager.aePic = aePic; | |
| 30 | +} | |
| 31 | + | |
| 32 | +//水印logo图片的位置和大小 | |
| 33 | +- (void)setLogoRect:(CGRect)logoRect { | |
| 34 | + _logoRect = logoRect; | |
| 35 | + STREAMER_Manager.logoRect = logoRect; | |
| 36 | +} | |
| 37 | + | |
| 38 | +//水印logo图片的透明度 | |
| 39 | +- (void)setLogoAlpha:(CGFloat)logoAlpha { | |
| 40 | + _logoAlpha = logoAlpha; | |
| 41 | + STREAMER_Manager.logoAlpha = logoAlpha; | |
| 42 | +} | |
| 43 | + | |
| 44 | +//文字的label | |
| 45 | +- (void)setTextLabel:(UILabel *)textLabel { | |
| 46 | + _textLabel = textLabel; | |
| 47 | + STREAMER_Manager.textLabel = textLabel; | |
| 48 | +} | |
| 49 | + | |
| 50 | +//文字label的位置和大小 | |
| 51 | +- (void)setTextRect:(CGRect)textRect { | |
| 52 | + _textRect = textRect; | |
| 53 | + STREAMER_Manager.textRect = textRect; | |
| 54 | +} | |
| 55 | + | |
| 56 | +//刷新文字的内容 | |
| 57 | +- (void)updateTextLabel { | |
| 58 | + [STREAMER_Manager updateTextLabel]; | |
| 59 | +} | |
| 60 | + | |
| 61 | +@end | ... | ... |
Example/CNLivePlayer.xcodeproj/project.pbxproj
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | |
| 9 | 9 | /* Begin PBXBuildFile section */ |
| 10 | 10 | 0807DF0E230799E000F409AC /* CNLivePlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0807DF0D230799E000F409AC /* CNLivePlayerController.m */; }; |
| 11 | + 085FA988230D34890072B936 /* CNLiveStreamerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 085FA987230D34890072B936 /* CNLiveStreamerController.m */; }; | |
| 11 | 12 | 086413BC22EAE229008713D3 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BB22EAE229008713D3 /* VideoToolbox.framework */; }; |
| 12 | 13 | 086413BE22EAE23E008713D3 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BD22EAE23E008713D3 /* libbz2.tbd */; }; |
| 13 | 14 | 086413C022EAE24F008713D3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BF22EAE24F008713D3 /* libz.tbd */; }; |
| ... | ... | @@ -45,6 +46,8 @@ |
| 45 | 46 | /* Begin PBXFileReference section */ |
| 46 | 47 | 0807DF0C230799E000F409AC /* CNLivePlayerController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLivePlayerController.h; sourceTree = "<group>"; }; |
| 47 | 48 | 0807DF0D230799E000F409AC /* CNLivePlayerController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNLivePlayerController.m; sourceTree = "<group>"; }; |
| 49 | + 085FA986230D34890072B936 /* CNLiveStreamerController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLiveStreamerController.h; sourceTree = "<group>"; }; | |
| 50 | + 085FA987230D34890072B936 /* CNLiveStreamerController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNLiveStreamerController.m; sourceTree = "<group>"; }; | |
| 48 | 51 | 086413BB22EAE229008713D3 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; |
| 49 | 52 | 086413BD22EAE23E008713D3 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; |
| 50 | 53 | 086413BF22EAE24F008713D3 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; |
| ... | ... | @@ -170,6 +173,8 @@ |
| 170 | 173 | 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */, |
| 171 | 174 | 0807DF0C230799E000F409AC /* CNLivePlayerController.h */, |
| 172 | 175 | 0807DF0D230799E000F409AC /* CNLivePlayerController.m */, |
| 176 | + 085FA986230D34890072B936 /* CNLiveStreamerController.h */, | |
| 177 | + 085FA987230D34890072B936 /* CNLiveStreamerController.m */, | |
| 173 | 178 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, |
| 174 | 179 | 6003F5A5195388D20070C39A /* CNLiveViewController.h */, |
| 175 | 180 | 6003F5A6195388D20070C39A /* CNLiveViewController.m */, |
| ... | ... | @@ -427,6 +432,7 @@ |
| 427 | 432 | 0807DF0E230799E000F409AC /* CNLivePlayerController.m in Sources */, |
| 428 | 433 | 6003F5A7195388D20070C39A /* CNLiveViewController.m in Sources */, |
| 429 | 434 | 6003F59A195388D20070C39A /* main.m in Sources */, |
| 435 | + 085FA988230D34890072B936 /* CNLiveStreamerController.m in Sources */, | |
| 430 | 436 | ); |
| 431 | 437 | runOnlyForDeploymentPostprocessing = 0; |
| 432 | 438 | }; | ... | ... |
Example/CNLivePlayer/CNLivePlayerController.m
| ... | ... | @@ -51,18 +51,23 @@ |
| 51 | 51 | // |
| 52 | 52 | // }]; |
| 53 | 53 | |
| 54 | - UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 55 | - btn.frame =CGRectMake(0, 0, 100, 100); | |
| 56 | - btn.center = self.view.center; | |
| 57 | - btn.backgroundColor = [UIColor redColor]; | |
| 58 | - [btn addTarget:self action:@selector(btnDidClicked) forControlEvents:UIControlEventTouchUpInside]; | |
| 59 | - [self.view addSubview:btn]; | |
| 54 | + UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 55 | + btn1.frame = CGRectMake(0, 0, 100, 50); | |
| 56 | + btn1.center = CGPointMake(self.view.center.x, self.view.center.y-50); | |
| 57 | + btn1.backgroundColor = [UIColor redColor]; | |
| 58 | + [btn1 setTitle:@"下一个" forState:UIControlStateNormal]; | |
| 59 | + [btn1 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 60 | + [btn1 addTarget:self action:@selector(btnDidClicked1) forControlEvents:UIControlEventTouchUpInside]; | |
| 61 | + [self.view addSubview:btn1]; | |
| 60 | 62 | |
| 61 | - UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 62 | - back.frame = CGRectMake(100, 600, 100, 100); | |
| 63 | - back.backgroundColor = [UIColor greenColor]; | |
| 64 | - [back addTarget:self action:@selector(backDidClicked) forControlEvents:UIControlEventTouchUpInside]; | |
| 65 | - [self.view addSubview:back]; | |
| 63 | + UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 64 | + btn2.frame = CGRectMake(0, 0, 100, 50); | |
| 65 | + btn2.center = CGPointMake(self.view.center.x, self.view.center.y+50); | |
| 66 | + btn2.backgroundColor = [UIColor redColor]; | |
| 67 | + [btn2 setTitle:@"返回" forState:UIControlStateNormal]; | |
| 68 | + [btn2 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 69 | + [btn2 addTarget:self action:@selector(btnDidClicked2) forControlEvents:UIControlEventTouchUpInside]; | |
| 70 | + [self.view addSubview:btn2]; | |
| 66 | 71 | // CNLivePlayerDownloader *dowa = [[CNLivePlayerDownloader alloc]initWithUrlString:@"http://wideo02.cnlive.com/video/data1/2017/0423/206473/800/c8ff347048414b7295cef882f8361073_206473_800.m3u8" progressBlock:^(float progress) { |
| 67 | 72 | // NSLog(@"缓存进度中%f",progress); |
| 68 | 73 | // }]; |
| ... | ... | @@ -88,7 +93,7 @@ |
| 88 | 93 | [_player destroyPlayer]; |
| 89 | 94 | } |
| 90 | 95 | |
| 91 | -- (void)btnDidClicked{ | |
| 96 | +- (void)btnDidClicked1{ | |
| 92 | 97 | __weak typeof(self) weakSelf = self; |
| 93 | 98 | // [_player setPlayerWithContentId:@"1066_716fabeaafb64236b60ecf74167c5c96" type:CNLivePlayerVod authSuccess:^{ |
| 94 | 99 | // __strong typeof(self) strongSelf = weakSelf; |
| ... | ... | @@ -108,7 +113,7 @@ |
| 108 | 113 | |
| 109 | 114 | } |
| 110 | 115 | |
| 111 | -- (void)backDidClicked{ | |
| 116 | +- (void)btnDidClicked2{ | |
| 112 | 117 | [self dismissViewControllerAnimated:YES completion:nil]; |
| 113 | 118 | |
| 114 | 119 | } | ... | ... |
CNLivePlayer/Classes/Streamer/CNLiveStreamer.h renamed to Example/CNLivePlayer/CNLiveStreamerController.h
| 1 | 1 | // |
| 2 | -// CNLiveStreamer.h | |
| 2 | +// CNLiveStreamerController.h | |
| 3 | 3 | // CNLivePlayer_Example |
| 4 | 4 | // |
| 5 | -// Created by CNLive-zxw on 2019/8/20. | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | 6 | // Copyright © 2019 153993236@qq.com. All rights reserved. |
| 7 | 7 | // |
| 8 | 8 | |
| 9 | -#import <Foundation/Foundation.h> | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | 10 | |
| 11 | 11 | NS_ASSUME_NONNULL_BEGIN |
| 12 | 12 | |
| 13 | -@interface CNLiveStreamer : NSObject | |
| 13 | +@interface CNLiveStreamerController : UIViewController | |
| 14 | 14 | |
| 15 | 15 | @end |
| 16 | 16 | ... | ... |
Example/CNLivePlayer/CNLiveStreamerController.m
0 → 100644
| 1 | +// | |
| 2 | +// CNLiveStreamerController.m | |
| 3 | +// CNLivePlayer_Example | |
| 4 | +// | |
| 5 | +// Created by CNLive-zxw on 2019/8/21. | |
| 6 | +// Copyright © 2019 153993236@qq.com. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "CNLiveStreamerController.h" | |
| 10 | + | |
| 11 | +@interface CNLiveStreamerController () | |
| 12 | + | |
| 13 | +@end | |
| 14 | + | |
| 15 | +@implementation CNLiveStreamerController | |
| 16 | + | |
| 17 | +- (void)viewDidLoad { | |
| 18 | + [super viewDidLoad]; | |
| 19 | + // Do any additional setup after loading the view. | |
| 20 | + self.view.backgroundColor = [UIColor whiteColor]; | |
| 21 | + UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 22 | + btn2.frame = CGRectMake(0, 0, 100, 50); | |
| 23 | + btn2.center = CGPointMake(self.view.center.x, self.view.center.y+50); | |
| 24 | + btn2.backgroundColor = [UIColor redColor]; | |
| 25 | + [btn2 setTitle:@"返回" forState:UIControlStateNormal]; | |
| 26 | + [btn2 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 27 | + [btn2 addTarget:self action:@selector(btnDidClicked2) forControlEvents:UIControlEventTouchUpInside]; | |
| 28 | + [self.view addSubview:btn2]; | |
| 29 | +} | |
| 30 | + | |
| 31 | + | |
| 32 | +- (void)btnDidClicked2{ | |
| 33 | + [self dismissViewControllerAnimated:YES completion:nil]; | |
| 34 | + | |
| 35 | +} | |
| 36 | + | |
| 37 | +/* | |
| 38 | +#pragma mark - Navigation | |
| 39 | + | |
| 40 | +// In a storyboard-based application, you will often want to do a little preparation before navigation | |
| 41 | +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
| 42 | + // Get the new view controller using [segue destinationViewController]. | |
| 43 | + // Pass the selected object to the new view controller. | |
| 44 | +} | |
| 45 | +*/ | |
| 46 | + | |
| 47 | +@end | ... | ... |
Example/CNLivePlayer/CNLiveViewController.m
| ... | ... | @@ -8,8 +8,8 @@ |
| 8 | 8 | |
| 9 | 9 | #import "CNLiveViewController.h" |
| 10 | 10 | #import "CNLivePlayerController.h" |
| 11 | -#import "CNLivePlayerDBTool.h" | |
| 12 | -#import "CNLiveHTTPProxyService.h" | |
| 11 | +#import "CNLiveStreamerController.h" | |
| 12 | + | |
| 13 | 13 | |
| 14 | 14 | @interface CNLiveViewController () |
| 15 | 15 | |
| ... | ... | @@ -17,33 +17,38 @@ |
| 17 | 17 | |
| 18 | 18 | @implementation CNLiveViewController |
| 19 | 19 | |
| 20 | --(void)viewWillAppear:(BOOL)animated{ | |
| 21 | - [super viewWillAppear:animated]; | |
| 22 | - NSMutableArray *data = [[CNLivePlayerDBTool shared] queryTable]; | |
| 23 | - for(CNLivePlayerDBModel *model in data){ | |
| 24 | - NSLog(@"%@---%@",model.contentId,model.url); | |
| 25 | - } | |
| 26 | - NSLog(@"--是否缓存--%d----",[[CNLiveHTTPProxyService manager]isCacheCompleteForUrl:[NSURL URLWithString:@"http://wideo02.cnlive.com/video/data1/2017/0423/206473/800/c8ff347048414b7295cef882f8361073_206473_800.m3u8"]]); | |
| 27 | - | |
| 28 | - | |
| 29 | -} | |
| 30 | 20 | - (void)viewDidLoad |
| 31 | 21 | { |
| 32 | 22 | [super viewDidLoad]; |
| 33 | 23 | // Do any additional setup after loading the view, typically from a nib. |
| 34 | 24 | |
| 35 | - UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 36 | - btn.frame =CGRectMake(0, 0, 100, 100); | |
| 37 | - btn.center = self.view.center; | |
| 38 | - btn.backgroundColor = [UIColor redColor]; | |
| 39 | - [btn addTarget:self action:@selector(btnDidClicked) forControlEvents:UIControlEventTouchUpInside]; | |
| 40 | - [self.view addSubview:btn]; | |
| 25 | + UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 26 | + btn1.frame = CGRectMake(0, 0, 100, 50); | |
| 27 | + btn1.center = CGPointMake(self.view.center.x, self.view.center.y-50); | |
| 28 | + btn1.backgroundColor = [UIColor redColor]; | |
| 29 | + [btn1 setTitle:@"播放" forState:UIControlStateNormal]; | |
| 30 | + [btn1 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 31 | + [btn1 addTarget:self action:@selector(btnDidClicked1) forControlEvents:UIControlEventTouchUpInside]; | |
| 32 | + [self.view addSubview:btn1]; | |
| 33 | + | |
| 34 | + UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 35 | + btn2.frame = CGRectMake(0, 0, 100, 50); | |
| 36 | + btn2.center = CGPointMake(self.view.center.x, self.view.center.y+50); | |
| 37 | + btn2.backgroundColor = [UIColor redColor]; | |
| 38 | + [btn2 setTitle:@"推流" forState:UIControlStateNormal]; | |
| 39 | + [btn2 setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 40 | + [btn2 addTarget:self action:@selector(btnDidClicked2) forControlEvents:UIControlEventTouchUpInside]; | |
| 41 | + [self.view addSubview:btn2]; | |
| 41 | 42 | |
| 42 | 43 | } |
| 43 | -- (void)btnDidClicked{ | |
| 44 | +- (void)btnDidClicked1{ | |
| 44 | 45 | [self presentViewController:[CNLivePlayerController new] animated:YES completion:nil]; |
| 45 | 46 | |
| 46 | 47 | } |
| 48 | +- (void)btnDidClicked2{ | |
| 49 | + [self presentViewController:[CNLiveStreamerController new] animated:YES completion:nil]; | |
| 50 | + | |
| 51 | +} | |
| 47 | 52 | |
| 48 | 53 | - (void)didReceiveMemoryWarning |
| 49 | 54 | { | ... | ... |