Commit 553be0d1b6075c72982114450ec8a8b9d0fb4cc6

Authored by zhangxiaowen
1 parent 565aa245

no message

CNLivePlayer/Classes/Header/CNLivePlayerDefines.h
@@ -43,4 +43,287 @@ typedef NS_ENUM(NSInteger, CNLivePlayerDownloaderState) { @@ -43,4 +43,287 @@ typedef NS_ENUM(NSInteger, CNLivePlayerDownloaderState) {
43 CNLivePlayerDownloaderFinished //完成 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 #endif /* CNLivePlayerDefines_h */ 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
1 -//  
2 -// CNLiveStreamer.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 "CNLiveStreamer.h"  
10 -  
11 -@implementation CNLiveStreamer  
12 -  
13 -@end  
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,6 +8,7 @@
8 8
9 /* Begin PBXBuildFile section */ 9 /* Begin PBXBuildFile section */
10 0807DF0E230799E000F409AC /* CNLivePlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0807DF0D230799E000F409AC /* CNLivePlayerController.m */; }; 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 086413BC22EAE229008713D3 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BB22EAE229008713D3 /* VideoToolbox.framework */; }; 12 086413BC22EAE229008713D3 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BB22EAE229008713D3 /* VideoToolbox.framework */; };
12 086413BE22EAE23E008713D3 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BD22EAE23E008713D3 /* libbz2.tbd */; }; 13 086413BE22EAE23E008713D3 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BD22EAE23E008713D3 /* libbz2.tbd */; };
13 086413C022EAE24F008713D3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BF22EAE24F008713D3 /* libz.tbd */; }; 14 086413C022EAE24F008713D3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 086413BF22EAE24F008713D3 /* libz.tbd */; };
@@ -45,6 +46,8 @@ @@ -45,6 +46,8 @@
45 /* Begin PBXFileReference section */ 46 /* Begin PBXFileReference section */
46 0807DF0C230799E000F409AC /* CNLivePlayerController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLivePlayerController.h; sourceTree = "<group>"; }; 47 0807DF0C230799E000F409AC /* CNLivePlayerController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLivePlayerController.h; sourceTree = "<group>"; };
47 0807DF0D230799E000F409AC /* CNLivePlayerController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNLivePlayerController.m; sourceTree = "<group>"; }; 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 086413BB22EAE229008713D3 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 51 086413BB22EAE229008713D3 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
49 086413BD22EAE23E008713D3 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; 52 086413BD22EAE23E008713D3 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; };
50 086413BF22EAE24F008713D3 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 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,6 +173,8 @@
170 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */, 173 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */,
171 0807DF0C230799E000F409AC /* CNLivePlayerController.h */, 174 0807DF0C230799E000F409AC /* CNLivePlayerController.h */,
172 0807DF0D230799E000F409AC /* CNLivePlayerController.m */, 175 0807DF0D230799E000F409AC /* CNLivePlayerController.m */,
  176 + 085FA986230D34890072B936 /* CNLiveStreamerController.h */,
  177 + 085FA987230D34890072B936 /* CNLiveStreamerController.m */,
173 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 178 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
174 6003F5A5195388D20070C39A /* CNLiveViewController.h */, 179 6003F5A5195388D20070C39A /* CNLiveViewController.h */,
175 6003F5A6195388D20070C39A /* CNLiveViewController.m */, 180 6003F5A6195388D20070C39A /* CNLiveViewController.m */,
@@ -427,6 +432,7 @@ @@ -427,6 +432,7 @@
427 0807DF0E230799E000F409AC /* CNLivePlayerController.m in Sources */, 432 0807DF0E230799E000F409AC /* CNLivePlayerController.m in Sources */,
428 6003F5A7195388D20070C39A /* CNLiveViewController.m in Sources */, 433 6003F5A7195388D20070C39A /* CNLiveViewController.m in Sources */,
429 6003F59A195388D20070C39A /* main.m in Sources */, 434 6003F59A195388D20070C39A /* main.m in Sources */,
  435 + 085FA988230D34890072B936 /* CNLiveStreamerController.m in Sources */,
430 ); 436 );
431 runOnlyForDeploymentPostprocessing = 0; 437 runOnlyForDeploymentPostprocessing = 0;
432 }; 438 };
Example/CNLivePlayer/CNLivePlayerController.m
@@ -51,18 +51,23 @@ @@ -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 // CNLivePlayerDownloader *dowa = [[CNLivePlayerDownloader alloc]initWithUrlString:@"http://wideo02.cnlive.com/video/data1/2017/0423/206473/800/c8ff347048414b7295cef882f8361073_206473_800.m3u8" progressBlock:^(float progress) { 71 // CNLivePlayerDownloader *dowa = [[CNLivePlayerDownloader alloc]initWithUrlString:@"http://wideo02.cnlive.com/video/data1/2017/0423/206473/800/c8ff347048414b7295cef882f8361073_206473_800.m3u8" progressBlock:^(float progress) {
67 // NSLog(@"缓存进度中%f",progress); 72 // NSLog(@"缓存进度中%f",progress);
68 // }]; 73 // }];
@@ -88,7 +93,7 @@ @@ -88,7 +93,7 @@
88 [_player destroyPlayer]; 93 [_player destroyPlayer];
89 } 94 }
90 95
91 -- (void)btnDidClicked{ 96 +- (void)btnDidClicked1{
92 __weak typeof(self) weakSelf = self; 97 __weak typeof(self) weakSelf = self;
93 // [_player setPlayerWithContentId:@"1066_716fabeaafb64236b60ecf74167c5c96" type:CNLivePlayerVod authSuccess:^{ 98 // [_player setPlayerWithContentId:@"1066_716fabeaafb64236b60ecf74167c5c96" type:CNLivePlayerVod authSuccess:^{
94 // __strong typeof(self) strongSelf = weakSelf; 99 // __strong typeof(self) strongSelf = weakSelf;
@@ -108,7 +113,7 @@ @@ -108,7 +113,7 @@
108 113
109 } 114 }
110 115
111 -- (void)backDidClicked{ 116 +- (void)btnDidClicked2{
112 [self dismissViewControllerAnimated:YES completion:nil]; 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 // CNLivePlayer_Example 3 // CNLivePlayer_Example
4 // 4 //
5 -// Created by CNLive-zxw on 2019/8/20. 5 +// Created by CNLive-zxw on 2019/8/21.
6 // Copyright © 2019 153993236@qq.com. All rights reserved. 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 NS_ASSUME_NONNULL_BEGIN 11 NS_ASSUME_NONNULL_BEGIN
12 12
13 -@interface CNLiveStreamer : NSObject 13 +@interface CNLiveStreamerController : UIViewController
14 14
15 @end 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 +8,8 @@
8 8
9 #import "CNLiveViewController.h" 9 #import "CNLiveViewController.h"
10 #import "CNLivePlayerController.h" 10 #import "CNLivePlayerController.h"
11 -#import "CNLivePlayerDBTool.h"  
12 -#import "CNLiveHTTPProxyService.h" 11 +#import "CNLiveStreamerController.h"
  12 +
13 13
14 @interface CNLiveViewController () 14 @interface CNLiveViewController ()
15 15
@@ -17,33 +17,38 @@ @@ -17,33 +17,38 @@
17 17
18 @implementation CNLiveViewController 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 - (void)viewDidLoad 20 - (void)viewDidLoad
31 { 21 {
32 [super viewDidLoad]; 22 [super viewDidLoad];
33 // Do any additional setup after loading the view, typically from a nib. 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 [self presentViewController:[CNLivePlayerController new] animated:YES completion:nil]; 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 - (void)didReceiveMemoryWarning 53 - (void)didReceiveMemoryWarning
49 { 54 {