KSYGPUStreamerKit.m 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  1. //
  2. // KSYGPUStreamerKit.m
  3. // KSYStreamer
  4. //
  5. // Created by pengbin on 09/01/16.
  6. // Copyright © 2016 ksyun. All rights reserved.
  7. //
  8. #import "KSYGPUStreamerKit.h"
  9. #define FLOAT_EQ( f0, f1 ) ( (f0 - f1 < 0.0001)&& (f0 - f1 > -0.0001) )
  10. #define weakObj(o) __weak typeof(o) o##Weak = o;
  11. @interface KSYGPUStreamerKit (){
  12. dispatch_queue_t _capDev_q;
  13. NSLock * _quitLock; // ensure capDev closed before dealloc
  14. CGFloat _previewRotateAng;
  15. int _autoRetryCnt;
  16. BOOL _bRetry;
  17. BOOL _bInterrupt;
  18. KSYDummyAudioSource *_dAudioSrc;
  19. // 音频采集模式(KSYAudioCapType)为AVCaptureDevice时发送静音包
  20. BOOL _bMute;
  21. KSYNetworkStatus _lastNetStatus;
  22. }
  23. // vMixerTargets
  24. @property (nonatomic, copy) NSArray *vPreviewTargets;
  25. @end
  26. @implementation KSYGPUStreamerKit
  27. /**
  28. @abstract 获取SDK版本号
  29. */
  30. - (NSString*) getKSYVersion {
  31. if (_streamerBase){
  32. return [_streamerBase getKSYVersion];
  33. }
  34. return @"KSY-i-v0.0.0";
  35. }
  36. /**
  37. @abstract 初始化方法
  38. @discussion 创建带有默认参数的 kit,不会打断其他后台的音乐播放
  39. @warning kit只支持单实例推流,构造多个实例会出现异常
  40. */
  41. - (instancetype) initWithDefaultCfg {
  42. return [self initInterrupt:NO];
  43. }
  44. /**
  45. @abstract 初始化方法
  46. @discussion 创建带有默认参数的 kit,会打断其他后台的音乐播放
  47. @warning kit只支持单实例推流,构造多个实例会出现异常
  48. */
  49. - (instancetype) initWithInterruptCfg {
  50. return [self initInterrupt:YES];
  51. }
  52. - (instancetype) initInterrupt:(BOOL) bInter {
  53. self = [super init];
  54. _quitLock = [[NSLock alloc] init];
  55. _capDev_q = dispatch_queue_create( "com.ksyun.capDev_q", DISPATCH_QUEUE_SERIAL);
  56. // init default property
  57. _bInterrupt = bInter;
  58. _captureState = KSYCaptureStateIdle;
  59. _capPreset = AVCaptureSessionPreset640x480;
  60. _videoFPS = 15;
  61. _previewDimension = CGSizeMake(640, 360);
  62. _streamDimension = CGSizeMake(640, 360);
  63. _cameraPosition = AVCaptureDevicePositionFront;
  64. _streamerMirrored = NO;
  65. _previewMirrored = NO;
  66. _previewRotateAng = 0;
  67. _videoProcessingCallback = nil;
  68. _audioProcessingCallback = nil;
  69. _interruptCallback = nil;
  70. _gpuOutputPixelFormat = kCVPixelFormatType_32BGRA;
  71. _capturePixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
  72. _audioDataType = KSYAudioData_CMSampleBuffer;
  73. _autoRetryCnt = 0;
  74. _maxAutoRetry = 0;
  75. _autoRetryDelay = 2.0;
  76. _bRetry = NO;
  77. // 图层和音轨的初始化
  78. _cameraLayer = 2;
  79. _logoPicLayer = 3;
  80. _logoTxtLayer = 4;
  81. _aeLayer = 6;
  82. _micTrack = 0;
  83. _bgmTrack = 1;
  84. /////1. 数据来源 ///////////
  85. _capToGpu = [[KSYGPUPicInput alloc] init];
  86. // 采集模块
  87. _vCapDev = [[KSYAVFCapture alloc] initWithSessionPreset:_capPreset
  88. cameraPosition:_cameraPosition];
  89. if(_vCapDev == nil) {
  90. return nil;
  91. }
  92. _vCapDev.outputImageOrientation =
  93. _previewOrientation =
  94. _videoOrientation =
  95. _streamOrientation = UIInterfaceOrientationPortrait;
  96. // 设置 AudioSession的属性为直播需要的默认值, 具体如下:
  97. // bInterruptOtherAudio : NO 不打断其他播放器
  98. // bDefaultToSpeaker : YES 背景音乐从外放播放
  99. // bAllowBluetooth : YES 启用蓝牙
  100. // AVAudioSessionCategory : AVAudioSessionCategoryPlayAndRecord 允许录音
  101. // 设置不打断其他播放器时,需要放在_aCapDev初始化前设置,否则没效果
  102. [[AVAudioSession sharedInstance] setDefaultCfg];
  103. [AVAudioSession sharedInstance].bInterruptOtherAudio = bInter;
  104. // 创建背景音乐播放模块
  105. _bgmPlayer = [[KSYBgmPlayer alloc] init];
  106. // 音频采集模块
  107. _aCapDev = [[KSYAUAudioCapture alloc] init];
  108. // 各种图片
  109. _logoPic = nil;
  110. _textPic = nil;
  111. _aePic = nil;
  112. _textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0, 360, 640)];
  113. _textLabel.textColor = [UIColor whiteColor];
  114. _textLabel.font = [UIFont fontWithName:@"Courier" size:20.0];
  115. _textLabel.backgroundColor = [UIColor clearColor];
  116. _textLabel.alpha = 1.0;
  117. /////2. 数据出口 ///////////
  118. // get pic data from gpu filter
  119. _gpuToStr =[[KSYGPUPicOutput alloc] init];
  120. // 创建 推流模块
  121. _streamerBase = [[KSYStreamerBase alloc] initWithDefaultCfg];
  122. // 创建 预览模块, 并放到视图底部
  123. _preview = [[KSYGPUView alloc] init];
  124. _preview.fillMode = kGPUImageFillModePreserveAspectRatioAndFill;
  125. ///// 3. 数据处理和通路 ///////////
  126. ///// 3.1 视频通路 ///////////
  127. // 核心部件:图像处理滤镜
  128. _filter = [[KSYGPUDnoiseFilter alloc] init];
  129. // 核心部件:视频叠加混合
  130. _vPreviewMixer = [[KSYGPUPicMixer alloc] init];
  131. _vStreamMixer = [[KSYGPUPicMixer alloc] init];
  132. // 组装视频通道
  133. [self setupVideoPath];
  134. // 初始化图层的位置
  135. self.logoRect = CGRectMake(0.1 , 0.05, 0, 0.1);
  136. self.textRect = CGRectMake(0.05, 0.15, 0, 20.0/640);
  137. ///// 3.2 音频通路 ///////////
  138. // 核心部件:音频叠加混合
  139. _aMixer = [[KSYAudioMixer alloc]init];
  140. _bStereoAudioStream = NO;
  141. // 组装音频通道
  142. [self setupAudioPath];
  143. //消息通道
  144. _msgStreamer = [[KSYMessage alloc] init];
  145. [self setupMessagePath];
  146. weakObj(self);
  147. _streamerBase.streamStateChange = ^(KSYStreamState state) {
  148. [selfWeak onStreamState:state];
  149. };
  150. _streamerBase.videoFPSChange = ^(int newVideoFPS){
  151. [selfWeak changeFPS:newVideoFPS];
  152. };
  153. //设置profile初始值
  154. self.streamerProfile = KSYStreamerProfile_540p_3;
  155. NSNotificationCenter* dc = [NSNotificationCenter defaultCenter];
  156. [dc addObserver:self
  157. selector:@selector(appBecomeActive)
  158. name:UIApplicationDidBecomeActiveNotification
  159. object:nil];
  160. [dc addObserver:self
  161. selector:@selector(appEnterBackground)
  162. name:UIApplicationDidEnterBackgroundNotification
  163. object:nil];
  164. [dc addObserver:self
  165. selector:@selector(onNetEvent)
  166. name:KSYNetStateEventNotification
  167. object:nil];
  168. return self;
  169. }
  170. - (instancetype)init {
  171. return [self initWithDefaultCfg];
  172. }
  173. - (void)dealloc {
  174. [_quitLock lock];
  175. [self closeKit];
  176. _msgStreamer = nil;
  177. _bgmPlayer = nil;
  178. _streamerBase = nil;
  179. _vCapDev = nil;
  180. _dAudioSrc = nil;
  181. [_quitLock unlock];
  182. _quitLock = nil;
  183. [[NSNotificationCenter defaultCenter] removeObserver:self];
  184. }
  185. /* reset all submodules */
  186. - (void) closeKit{
  187. [_bgmPlayer stopPlayBgm];
  188. [_streamerBase stopStream];
  189. [_aCapDev stopCapture];
  190. [_vCapDev stopCameraCapture];
  191. [_vCapDev removeAudioInputsAndOutputs];
  192. [_capToGpu removeAllTargets];
  193. [_filter removeAllTargets];
  194. [_logoPic removeAllTargets];
  195. [_textPic removeAllTargets];
  196. [_aePic removeAllTargets];
  197. [_vPreviewMixer removeAllTargets];
  198. [_vStreamMixer removeAllTargets];
  199. }
  200. /**
  201. @abstract 设置当前使用的滤镜
  202. @discussion 若filter 为nil, 则关闭滤镜
  203. @discussion 若filter 为GPUImageFilter的实例,则使用该滤镜做处理
  204. @discussion filter 也可以是GPUImageFilterGroup的实例,可以将多个滤镜组合
  205. @see GPUImageFilter
  206. */
  207. - (void) setupFilter:(GPUImageOutput<GPUImageInput> *) filter {
  208. _filter = filter;
  209. if (_vCapDev == nil) {
  210. return;
  211. }
  212. // 采集的图像先经过前处理
  213. [_capToGpu removeAllTargets];
  214. GPUImageOutput* src = _capToGpu;
  215. if (_filter) {
  216. [_filter removeAllTargets];
  217. [src addTarget:_filter];
  218. src = _filter;
  219. }
  220. // 组装图层
  221. _vPreviewMixer.masterLayer = _cameraLayer;
  222. _vStreamMixer.masterLayer = _cameraLayer;
  223. [self addPic:src ToMixerAt:_cameraLayer];
  224. [self addPic:_logoPic ToMixerAt:_logoPicLayer];
  225. [self addPic:_textPic ToMixerAt:_logoTxtLayer];
  226. self.aePic = _aePic;
  227. [self setPreviewMirrored: _previewMirrored];
  228. [self setStreamerMirrored: _streamerMirrored];
  229. }
  230. - (void) setupVMixer {
  231. if (_vPreviewMixer.targets.count > 0 && _vPreviewTargets.count == 0) {
  232. _vPreviewTargets = [_vPreviewMixer.targets copy];
  233. }
  234. // 混合后的图像输出到预览和推流
  235. [_vPreviewMixer removeAllTargets];
  236. if (![_vPreviewTargets containsObject:_preview]) {
  237. [_vPreviewMixer addTarget:_preview];
  238. }else{
  239. for (id<GPUImageInput> target in _vPreviewTargets) {
  240. [_vPreviewMixer addTarget:target];
  241. }
  242. }
  243. _vPreviewTargets = nil;
  244. [_vStreamMixer removeAllTargets];
  245. [_vStreamMixer addTarget:_gpuToStr];
  246. // 设置镜像
  247. [self setPreviewOrientation:_previewOrientation];
  248. [self setStreamOrientation:_streamOrientation];
  249. }
  250. // 添加图层到 vMixer 中
  251. - (void) addPic:(KSYGPUPicture*)pic ToMixerAt: (NSInteger)idx{
  252. KSYGPUPicMixer * vMixer[2] = {_vPreviewMixer, _vStreamMixer};
  253. if (pic == nil){
  254. for (int i = 0; i<2; ++i) {
  255. [vMixer[i] clearPicOfLayer:idx];
  256. }
  257. return;
  258. }
  259. [pic removeAllTargets];
  260. for (int i = 0; i<2; ++i) {
  261. [pic addTarget:vMixer[i] atTextureLocation:idx];
  262. }
  263. }
  264. // 组装视频通道
  265. - (void) setupVideoPath {
  266. weakObj(self);
  267. // 前处理 和 图像 mixer
  268. [self setupFilter:_filter];
  269. [self setupVMixer];
  270. // 采集到的画面上传GPU
  271. _vCapDev.videoProcessingCallback = ^(CMSampleBufferRef buf) {
  272. if ( selfWeak.videoProcessingCallback ){
  273. selfWeak.videoProcessingCallback(buf);
  274. }
  275. [selfWeak.capToGpu processSampleBuffer:buf];
  276. };
  277. // GPU 上的数据导出到streamer
  278. _gpuToStr.videoProcessingCallback = ^(CVPixelBufferRef pixelBuffer, CMTime timeInfo){
  279. if (![selfWeak.streamerBase isStreaming]){
  280. return;
  281. }
  282. [selfWeak.streamerBase processVideoPixelBuffer:pixelBuffer
  283. timeInfo:timeInfo];
  284. };
  285. // 采集被打断的事件回调
  286. _vCapDev.interruptCallback = ^(BOOL bInterrupt) {
  287. if (bInterrupt) {
  288. [selfWeak appEnterBackground];
  289. }
  290. else {
  291. [selfWeak appBecomeActive];
  292. }
  293. if(selfWeak.interruptCallback) {
  294. selfWeak.interruptCallback(bInterrupt);
  295. }
  296. };
  297. }
  298. // 将声音送入混音器
  299. - (void) mixAudio:(CMSampleBufferRef)buf to:(int)idx{
  300. if (![_streamerBase isStreaming]){
  301. return;
  302. }
  303. [_aMixer processAudioSampleBuffer:buf of:idx];
  304. }
  305. // 组装声音通道
  306. - (void) setupAudioPath {
  307. weakObj(self);
  308. //1. 音频采集, 语音数据送入混音器
  309. if (_audioDataType == KSYAudioData_CMSampleBuffer) {
  310. _aCapDev.audioProcessingCallback = ^(CMSampleBufferRef buf){
  311. if ( selfWeak.audioProcessingCallback ){
  312. selfWeak.audioProcessingCallback(buf);
  313. }
  314. [selfWeak mixAudio:buf to:selfWeak.micTrack];
  315. };
  316. }
  317. else {
  318. _aCapDev.pcmProcessingCallback = ^(uint8_t **pData, int len, const AudioStreamBasicDescription *fmt, CMTime timeInfo) {
  319. if ( selfWeak.pcmProcessingCallback ){
  320. selfWeak.pcmProcessingCallback(pData, len, fmt, timeInfo);
  321. }
  322. if (![selfWeak.streamerBase isStreaming]){
  323. return;
  324. }
  325. [selfWeak.aMixer processAudioData:pData nbSample:len withFormat:fmt timeinfo:timeInfo of:selfWeak.micTrack];
  326. };
  327. }
  328. //2. 背景音乐播放,音乐数据送入混音器
  329. _bgmPlayer.audioDataBlock = ^ BOOL(uint8_t** pData, int len, const AudioStreamBasicDescription* fmt, CMTime pts){
  330. if ([selfWeak.streamerBase isStreaming]) {
  331. [selfWeak.aMixer processAudioData:pData
  332. nbSample:len
  333. withFormat:fmt
  334. timeinfo:pts
  335. of:selfWeak.bgmTrack];
  336. }
  337. return YES;
  338. };
  339. // 混音结果送入streamer
  340. if (_audioDataType == KSYAudioData_CMSampleBuffer) {
  341. _aMixer.audioProcessingCallback = ^(CMSampleBufferRef buf){
  342. if (![selfWeak.streamerBase isStreaming]){
  343. return;
  344. }
  345. [selfWeak.streamerBase processAudioSampleBuffer:buf];
  346. };
  347. }
  348. else {
  349. _aMixer.pcmProcessingCallback = ^(uint8_t **pData, int nbSample, CMTime pts) {
  350. if (![selfWeak.streamerBase isStreaming]){
  351. return;
  352. }
  353. [selfWeak.streamerBase processAudioData:pData
  354. nbSample:nbSample
  355. withFormat:selfWeak.aMixer.outFmtDes
  356. timeinfo:&pts];
  357. };
  358. }
  359. // mixer 的主通道为麦克风,时间戳以main通道为准
  360. _aMixer.mainTrack = _micTrack;
  361. [_aMixer setTrack:_micTrack enable:YES];
  362. [_aMixer setTrack:_bgmTrack enable:YES];
  363. }
  364. #pragma mark - message
  365. - (void) setupMessagePath {
  366. weakObj(self);
  367. _msgStreamer.messageProcessingCallback = ^(NSDictionary *messageData){
  368. [selfWeak.streamerBase processMessageData:messageData];
  369. };
  370. }
  371. - (BOOL) processMessageData:(NSDictionary *)messageData{
  372. if(_msgStreamer)
  373. return [_msgStreamer processMessageData:messageData];
  374. return NO;
  375. }
  376. #pragma mark - 状态通知
  377. - (void) newCaptureState:(KSYCaptureState) state {
  378. dispatch_async(dispatch_get_main_queue(), ^{
  379. _captureState = state;
  380. NSNotificationCenter* dc =[NSNotificationCenter defaultCenter];
  381. [dc postNotificationName:KSYCaptureStateDidChangeNotification
  382. object:self];
  383. });
  384. }
  385. #define CASE_RETURN( ENU ) case ENU : {return @#ENU;}
  386. /**
  387. @abstract 获取采集状态对应的字符串
  388. */
  389. - (NSString*) getCaptureStateName: (KSYCaptureState) stat{
  390. switch (stat){
  391. CASE_RETURN(KSYCaptureStateIdle)
  392. CASE_RETURN(KSYCaptureStateCapturing)
  393. CASE_RETURN(KSYCaptureStateDevAuthDenied)
  394. CASE_RETURN(KSYCaptureStateClosingCapture)
  395. CASE_RETURN(KSYCaptureStateParameterError)
  396. CASE_RETURN(KSYCaptureStateDevBusy)
  397. default: { return @"unknow"; }
  398. }
  399. }
  400. - (NSString*) getCurCaptureStateName {
  401. return [self getCaptureStateName:_captureState];
  402. }
  403. #pragma mark - capture actions
  404. /**
  405. @abstract 启动预览
  406. @param view 预览画面作为subview,插入到 view 的最底层
  407. @discussion 设置完成采集参数之后,按照设置值启动预览,启动后对采集参数修改不会生效
  408. @discussion 需要访问摄像头和麦克风的权限,若授权失败,其他API都会拒绝服务
  409. @warning: 开始推流前必须先启动预览
  410. @see videoDimension, cameraPosition, videoOrientation, videoFPS
  411. */
  412. - (void) startPreview: (UIView*) view {
  413. if (_capDev_q == nil || view == nil || [_vCapDev isRunning]) {
  414. return;
  415. }
  416. dispatch_async(dispatch_get_main_queue(), ^(){
  417. [view addSubview:_preview];
  418. [view sendSubviewToBack:_preview];
  419. _preview.frame = view.bounds;
  420. if ([self startVideoCap] == NO){
  421. return;
  422. }
  423. if ([self startAudioCap] == NO){
  424. return;
  425. }
  426. });
  427. }
  428. /**
  429. @abstract 开启视频配置和采集
  430. @discussion 设置完成视频采集参数之后,按照设置值启动视频预览,启动后对视频采集参数修改不会生效
  431. @discussion 需要访问摄像头的权限,若授权失败,其他API都会拒绝服务
  432. @discussion 视频采集成功返回YES,不成功返回NO
  433. */
  434. - (BOOL) startVideoCap{
  435. AVAuthorizationStatus status_video = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  436. if ( status_video == AVAuthorizationStatusDenied == AVAuthorizationStatusDenied) {
  437. [self newCaptureState:KSYCaptureStateDevAuthDenied];
  438. return NO;
  439. }
  440. if (_capPreset == nil) {
  441. [self newCaptureState:KSYCaptureStateParameterError];
  442. return NO;
  443. }
  444. dispatch_async(_capDev_q, ^{
  445. [_quitLock lock];
  446. if ( _cameraPosition != [_vCapDev cameraPosition] ){
  447. [_vCapDev rotateCamera];
  448. }
  449. _vCapDev.captureSessionPreset = _capPreset;
  450. // check if preset ok
  451. _capPreset = _vCapDev.captureSessionPreset;
  452. [self updatePreDimension];
  453. [self updateStrDimension:self.videoOrientation];
  454. // 旋转
  455. [self rotatePreviewTo:_videoOrientation ];
  456. [self rotateStreamTo: _videoOrientation ];
  457. // 连接
  458. [self setupFilter:_filter];
  459. [self setupVMixer];
  460. // 开始预览
  461. [_vCapDev startCameraCapture];
  462. [_quitLock unlock];
  463. [self newCaptureState:KSYCaptureStateCapturing];
  464. });
  465. return YES;
  466. }
  467. /**
  468. @abstract 开始音频配置和采集
  469. @discussion 设置完成音频采集参数之后,按照设置值启动音频预览,启动后对音频采集参数修改不会生效
  470. @discussion 需要访问麦克风的权限,若授权失败,其他API都会拒绝服务
  471. @discussion 音频采集成功返回YES,不成功返回NO
  472. */
  473. - (BOOL) startAudioCap{
  474. AVAuthorizationStatus status_audio = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  475. if ( status_audio == AVAuthorizationStatusDenied) {
  476. [self newCaptureState:KSYCaptureStateDevAuthDenied];
  477. return NO;
  478. }
  479. dispatch_async(_capDev_q, ^{
  480. [_quitLock lock];
  481. //配置audioSession的方法由init移入startPreview,防止在init之后,startPreview之前被外部修改
  482. [AVAudioSession sharedInstance].bInterruptOtherAudio = _bInterrupt;
  483. [_aCapDev startCapture];
  484. [_quitLock unlock];
  485. [self newCaptureState:KSYCaptureStateCapturing];
  486. });
  487. return YES;
  488. }
  489. /**
  490. @abstract 停止预览,停止采集设备,并清理会话(step5)
  491. @discussion 若推流未结束,则先停止推流
  492. @see stopStream
  493. */
  494. - (void) stopPreview {
  495. if (_vCapDev== nil ) {
  496. return;
  497. }
  498. [self newCaptureState:KSYCaptureStateClosingCapture];
  499. dispatch_async(_capDev_q, ^{
  500. [_quitLock lock];
  501. [self closeKit];
  502. dispatch_async(dispatch_get_main_queue(), ^{
  503. if (_preview){
  504. [_preview removeFromSuperview];
  505. }
  506. });
  507. [_quitLock unlock];
  508. [self newCaptureState:KSYCaptureStateIdle];
  509. });
  510. }
  511. /** 进入后台 */
  512. - (void) appEnterBackground {
  513. if (_vPreviewMixer.targets.count > 0){
  514. _vPreviewTargets = [_vPreviewMixer.targets copy];
  515. }
  516. // 进入后台时, 将预览从图像混合器中脱离, 避免后台OpenGL渲染崩溃
  517. [_vPreviewMixer removeAllTargets];
  518. if (_audioCaptureType == KSYAudioCap_AVCaptureDevice) {
  519. [self startDummySource];
  520. }
  521. // 重复最后一帧视频图像
  522. _gpuToStr.bAutoRepeat = YES;
  523. if (_streamerBase.bypassRecordState == KSYRecordStateRecording ) {
  524. [_streamerBase stopBypassRecord];
  525. }
  526. }
  527. /** 回到前台 */
  528. - (void) appBecomeActive{
  529. // 回到前台, 重新连接预览
  530. [self setupVMixer];
  531. [_aCapDev resumeCapture];
  532. if (_audioCaptureType == KSYAudioCap_AVCaptureDevice) {
  533. _bMute = NO;
  534. // 停止 dummy audio source
  535. if ([_dAudioSrc bRunning]) {
  536. [_dAudioSrc stop];
  537. _dAudioSrc.audioProcessingCallback = nil;
  538. }
  539. }
  540. if (!_streamerFreezed) {
  541. _gpuToStr.bAutoRepeat = NO;
  542. }
  543. }
  544. - (void)startDummySource{
  545. weakObj(self);
  546. _bMute = YES;
  547. // 开启后台任务,避免被suspend
  548. __block UIBackgroundTaskIdentifier background_task;
  549. dispatch_queue_t back_task_queue = dispatch_queue_create("com.ksyun.backgroundTask.queue", DISPATCH_QUEUE_SERIAL);
  550. background_task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ {
  551. [[UIApplication sharedApplication] endBackgroundTask:background_task];
  552. background_task = UIBackgroundTaskInvalid;
  553. }];
  554. dispatch_async(back_task_queue, ^{
  555. while (_bMute) {
  556. sleep(1);
  557. }
  558. [[UIApplication sharedApplication] endBackgroundTask:background_task];
  559. background_task = UIBackgroundTaskInvalid;
  560. });
  561. // 开启 dummy audio source
  562. _dAudioSrc.audioProcessingCallback = ^(CMSampleBufferRef buf) {
  563. if (selfWeak.audioProcessingCallback && _bMute) {
  564. selfWeak.audioProcessingCallback( buf);
  565. }
  566. };
  567. [_dAudioSrc start];
  568. }
  569. #pragma mark - try reconnect
  570. - (void) onStreamState : (KSYStreamState) stat {
  571. if (stat == KSYStreamStateError){
  572. [self onStreamError:_streamerBase.streamErrorCode];
  573. }
  574. else if (stat == KSYStreamStateConnected){
  575. _autoRetryCnt = _maxAutoRetry;
  576. _bRetry = NO;
  577. }
  578. }
  579. - (void) onStreamError: (KSYStreamErrorCode) errCode {
  580. NSString * name = [_streamerBase getCurKSYStreamErrorCodeName];
  581. NSLog(@"stream Error: %@", [name substringFromIndex:19]);
  582. if (errCode == KSYStreamErrorCode_CONNECT_BREAK ||
  583. errCode == KSYStreamErrorCode_AV_SYNC_ERROR ||
  584. errCode == KSYStreamErrorCode_Connect_Server_failed ||
  585. errCode == KSYStreamErrorCode_DNS_Parse_failed ||
  586. errCode == KSYStreamErrorCode_CODEC_OPEN_FAILED) {
  587. if (_bRetry == NO){
  588. [self tryReconnect];
  589. }
  590. }
  591. }
  592. - (void) onNetEvent {
  593. KSYNetStateCode code = [_streamerBase netStateCode];
  594. if (code == KSYNetStateCode_REACHABLE) {
  595. if ( _streamerBase.streamState == KSYStreamStateError) {
  596. [self tryReconnect];
  597. }
  598. KSYNetworkStatus curStat = _streamerBase.netReachability.currentReachabilityStatus;
  599. if (_lastNetStatus == KSYReachableViaWWAN &&
  600. curStat == KSYReachableViaWiFi) { // 4G to wifi
  601. NSLog(@"warning: 4Gtowifi: still using 4G!");
  602. }
  603. _lastNetStatus = curStat;
  604. }
  605. else if (code == KSYNetStateCode_UNREACHABLE) {
  606. _lastNetStatus = _streamerBase.netReachability.currentReachabilityStatus;
  607. }
  608. }
  609. - (void) tryReconnect {
  610. _bRetry = YES;
  611. int64_t delaySec = (int64_t)(_autoRetryDelay * NSEC_PER_SEC);
  612. dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, delaySec);
  613. dispatch_after(delay, dispatch_get_main_queue(), ^{
  614. _bRetry = NO;
  615. if (_autoRetryCnt <= 0 || _streamerBase.netReachState == KSYNetReachState_Bad) {
  616. return;
  617. }
  618. if (!_streamerBase.isStreaming) {
  619. NSLog(@"retry connect %d/%d", _autoRetryCnt, _maxAutoRetry);
  620. _autoRetryCnt--;
  621. [_streamerBase startStream:_streamerBase.hostURL];
  622. }
  623. });
  624. }
  625. @synthesize autoRetryDelay = _autoRetryDelay;
  626. -(void) setAutoRetryDelay:(double)autoRetryDelay {
  627. _autoRetryDelay = MAX(0.1, autoRetryDelay);
  628. }
  629. @synthesize maxAutoRetry = _maxAutoRetry;
  630. -(void) setMaxAutoRetry:(int)maxAutoRetry {
  631. _maxAutoRetry = MAX(0, maxAutoRetry);
  632. _autoRetryCnt = _maxAutoRetry;
  633. }
  634. @synthesize audioDataType = _audioDataType;
  635. - (void) setAudioDataType:(KSYAudioDataType)audioDataType {
  636. _audioDataType = audioDataType;
  637. [self setupAudioPath];
  638. }
  639. - (KSYAudioDataType) audioDataType {
  640. return _audioDataType;
  641. }
  642. #pragma mark - Dimension
  643. /**
  644. @abstract 查询实际的采集分辨率
  645. @discussion 参见iOS的 AVCaptureSessionPresetXXX的定义
  646. */
  647. - (CGSize) captureDimension {
  648. if (_vCapDev){
  649. return _vCapDev.captureDimension;
  650. }
  651. return CGSizeZero;
  652. }
  653. // 根据朝向, 判断是否需要交换宽和高
  654. -(CGSize) getDimension: (CGSize) sz
  655. byOriention: (UIInterfaceOrientation) ori {
  656. CGSize outSz = sz;
  657. if ( ( ori == UIInterfaceOrientationPortraitUpsideDown ||
  658. ori == UIInterfaceOrientationPortrait )) {
  659. outSz.height = MAX(sz.width, sz.height);
  660. outSz.width = MIN(sz.width, sz.height);
  661. }
  662. else {
  663. outSz.height = MIN(sz.width, sz.height);
  664. outSz.width = MAX(sz.width, sz.height);
  665. }
  666. return outSz;
  667. }
  668. // 居中裁剪
  669. -(CGRect) calcCropRect: (CGSize) camSz to: (CGSize) outSz {
  670. double x = (camSz.width -outSz.width )/2/camSz.width;
  671. double y = (camSz.height -outSz.height)/2/camSz.height;
  672. double wdt = outSz.width/camSz.width;
  673. double hgt = outSz.height/camSz.height;
  674. return CGRectMake(x, y, wdt, hgt);
  675. }
  676. // 对 inSize 按照 targetSz的宽高比 进行裁剪, 得到最大的输出size
  677. -(CGSize) calcCropSize: (CGSize) inSz to: (CGSize) targetSz {
  678. CGFloat preRatio = targetSz.width / targetSz.height;
  679. CGSize cropSz = inSz; // set width
  680. cropSz.height = cropSz.width / preRatio;
  681. if (cropSz.height > inSz.height){
  682. cropSz.height = inSz.height; // set height
  683. cropSz.width = cropSz.height * preRatio;
  684. }
  685. return cropSz;
  686. }
  687. // 更新分辨率相关设置
  688. // 根据宽高比计算需要裁剪掉的区域
  689. - (void) updatePreDimension {
  690. _previewDimension = [self getDimension:_previewDimension
  691. byOriention:_videoOrientation];
  692. CGSize inSz = [self captureDimension];
  693. inSz = [self getDimension:inSz byOriention:_vCapDev.outputImageOrientation];
  694. CGSize cropSz = [self calcCropSize:inSz to:_previewDimension];
  695. _capToGpu.cropRegion = [self calcCropRect:inSz to:cropSz];
  696. _capToGpu.outputRotation = kGPUImageNoRotation;
  697. [_capToGpu forceProcessingAtSize:_previewDimension];
  698. }
  699. - (void) updateStrDimension:(UIInterfaceOrientation) orie {
  700. _streamDimension = [self getDimension:_streamDimension
  701. byOriention:orie];
  702. _gpuToStr.bCustomOutputSize = YES;
  703. _gpuToStr.outputSize = _streamDimension;
  704. CGSize preSz = [self getDimension:_previewDimension
  705. byOriention:orie];
  706. CGSize cropSz = [self calcCropSize:preSz
  707. to:_streamDimension];
  708. _gpuToStr.cropRegion = [self calcCropRect:preSz
  709. to:cropSz];
  710. }
  711. // 分辨率有效范围检查
  712. @synthesize previewDimension = _previewDimension;
  713. - (void) setPreviewDimension:(CGSize) sz{
  714. _previewDimension.width = MAX(sz.width, sz.height);
  715. _previewDimension.height = MIN(sz.width, sz.height);
  716. _previewDimension.width = MAX(160, MIN(_previewDimension.width, 1920));
  717. _previewDimension.height = MAX( 90, MIN(_previewDimension.height,1080));
  718. }
  719. @synthesize streamDimension = _streamDimension;
  720. - (void) setStreamDimension:(CGSize) sz{
  721. _streamDimension.width = MAX(sz.width, sz.height);
  722. _streamDimension.height = MIN(sz.width, sz.height);
  723. _streamDimension.width = MAX(160, MIN(_streamDimension.width, 1280));
  724. _streamDimension.height = MAX( 90, MIN(_streamDimension.height, 720));
  725. }
  726. @synthesize videoFPS = _videoFPS;
  727. - (void)changeFPS:(int)fps {
  728. _videoFPS = MAX(1, MIN(fps, 30));
  729. _vCapDev.frameRate = _videoFPS;
  730. _streamerBase.videoFPS = _videoFPS;
  731. }
  732. - (void) setVideoFPS: (int) fps {
  733. if(_captureState == KSYCaptureStateIdle)
  734. {
  735. [self changeFPS:fps];
  736. }
  737. }
  738. @synthesize videoOrientation = _videoOrientation;
  739. - (void) setVideoOrientation: (UIInterfaceOrientation) orie {
  740. if (_vCapDev.isRunning){
  741. return;
  742. }
  743. _vCapDev.outputImageOrientation =
  744. _previewOrientation =
  745. _streamOrientation =
  746. _videoOrientation = orie;
  747. }
  748. - (UIInterfaceOrientation) videoOrientation{
  749. return _vCapDev.outputImageOrientation;
  750. }
  751. @synthesize bStereoAudioStream = _bStereoAudioStream;
  752. - (void) setBStereoAudioStream:(BOOL)bStereoAudioStream {
  753. if (_streamerBase.isStreaming){
  754. return; // 推流过程中修改本属性会导致观众端的声音异常
  755. }
  756. _bStereoAudioStream =
  757. _aMixer.bStereo = bStereoAudioStream;
  758. }
  759. -(BOOL) bStereoAudioStream {
  760. return _aMixer.bStereo;
  761. }
  762. /**
  763. @abstract 切换摄像头
  764. @return TRUE: 成功切换摄像头, FALSE:当前参数,下一个摄像头不支持,切换失败
  765. @discussion 在前后摄像头间切换,从当前的摄像头切换到另一个,切换成功则修改cameraPosition的值
  766. @discussion 开始预览后开始有效,推流过程中也响应切换请求
  767. @see cameraPosition
  768. */
  769. - (BOOL) switchCamera{
  770. if (_vCapDev == nil) {
  771. return NO;
  772. }
  773. _cameraPosition = _vCapDev.cameraPosition;
  774. [_vCapDev rotateCamera];
  775. if (_cameraPosition == _vCapDev.cameraPosition) {
  776. return NO;
  777. }
  778. _cameraPosition = _vCapDev.cameraPosition;
  779. return YES;
  780. }
  781. /**
  782. @abstract 当前采集设备是否支持闪光灯
  783. @return YES / NO
  784. @discussion 通常只有后置摄像头支持闪光灯
  785. @see setTorchMode
  786. */
  787. - (BOOL) isTorchSupported{
  788. if (_vCapDev){
  789. return _vCapDev.isTorchSupported;
  790. }
  791. return NO;
  792. }
  793. /**
  794. @abstract 开关闪光灯
  795. @discussion 切换闪光灯的开关状态 开 <--> 关
  796. @see setTorchMode
  797. */
  798. - (void) toggleTorch {
  799. if (_vCapDev){
  800. [_vCapDev toggleTorch];
  801. }
  802. }
  803. /**
  804. @abstract 设置闪光灯
  805. @param mode AVCaptureTorchModeOn/Off
  806. @discussion 设置闪光灯的开关状态
  807. @discussion 开始预览后开始有效
  808. @see AVCaptureTorchMode
  809. */
  810. - (void) setTorchMode: (AVCaptureTorchMode)mode{
  811. if (_vCapDev){
  812. [_vCapDev setTorchMode:mode];
  813. }
  814. }
  815. /**
  816. @abstract 获取当前采集设备的指针
  817. @discussion 开放本指针的目的是开放类似下列添加到AVCaptureDevice的 categories:
  818. - AVCaptureDeviceFlash
  819. - AVCaptureDeviceTorch
  820. - AVCaptureDeviceFocus
  821. - AVCaptureDeviceExposure
  822. - AVCaptureDeviceWhiteBalance
  823. - etc.
  824. @return AVCaptureDevice* 预览开始前调用返回为nil,开始预览后,返回当前正在使用的摄像头
  825. @warning 请勿修改摄像头的像素格式,帧率,分辨率等参数,修改后会导致推流工作异常或崩溃
  826. @see AVCaptureDevice AVCaptureDeviceTorch AVCaptureDeviceFocus
  827. */
  828. - (AVCaptureDevice*) getCurrentCameraDevices {
  829. if (_vCapDev){
  830. return _vCapDev.inputCamera;
  831. }
  832. return nil;
  833. }
  834. #pragma mark - utils
  835. -(UIImage *)imageFromUIView:(UIView *)v {
  836. CGSize s = v.frame.size;
  837. UIGraphicsBeginImageContextWithOptions(s, NO, 0.0);
  838. [v.layer renderInContext:UIGraphicsGetCurrentContext()];
  839. UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
  840. UIGraphicsEndImageContext();
  841. return image;
  842. }
  843. #pragma mark - pictures & logo
  844. @synthesize textPic = _textPic;
  845. -(void) setTextPic:(KSYGPUPicture *)textPic{
  846. _textPic = textPic;
  847. [self addPic:_textPic ToMixerAt:_logoTxtLayer];
  848. }
  849. @synthesize logoPic = _logoPic;
  850. -(void) setLogoPic:(KSYGPUPicture *)pic{
  851. _logoPic = pic;
  852. [self addPic:_logoPic ToMixerAt:_logoPicLayer];
  853. }
  854. static GPUImageRotationMode KSYImage2GPURotate[] = {
  855. kGPUImageNoRotation,// UIImageOrientationUp, // default orientation
  856. kGPUImageRotate180,//UIImageOrientationDown, // 180 deg rotation
  857. kGPUImageRotateLeft, //UIImageOrientationLeft, // 90 deg CCW
  858. kGPUImageRotateRight,//UIImageOrientationRight, // 90 deg CW
  859. kGPUImageFlipHorizonal,//UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip
  860. kGPUImageFlipHorizonal,//UIImageOrientationDownMirrored, // horizontal flip
  861. kGPUImageFlipVertical,//UIImageOrientationLeftMirrored, // vertical flip
  862. kGPUImageRotateRightFlipVertical//UIImageOrientationRightMirrored, // vertical flip
  863. };
  864. - (void) setOrientaion:(UIImageOrientation) orien ofLayer:(NSInteger)idx {
  865. [_vPreviewMixer setPicRotation:KSYImage2GPURotate[orien]
  866. ofLayer:idx];
  867. [_vStreamMixer setPicRotation:KSYImage2GPURotate[orien]
  868. ofLayer:idx];
  869. }
  870. - (void) setLogoOrientaion:(UIImageOrientation) orien{
  871. [self setOrientaion:orien ofLayer:_logoPicLayer];
  872. }
  873. @synthesize aePic = _aePic;
  874. -(void) setAePic:(GPUImageUIElement *)aePic{
  875. _aePic = aePic;
  876. [self.vStreamMixer clearPicOfLayer:_aeLayer];
  877. if (_aePic == nil){
  878. return;
  879. }
  880. [_aePic removeAllTargets];
  881. [_aePic addTarget:self.vStreamMixer atTextureLocation:_aeLayer];
  882. }
  883. - (void) setRect:(CGRect) rect ofLayer:(NSInteger)idx {
  884. [_vPreviewMixer setPicRect:rect
  885. ofLayer:idx];
  886. [_vStreamMixer setPicRect:rect
  887. ofLayer:idx];
  888. }
  889. // 水印logo的图片的位置和大小
  890. @synthesize logoRect = _logoRect;
  891. - (CGRect) logoRect {
  892. return [_vPreviewMixer getPicRectOfLayer:_logoPicLayer];
  893. }
  894. - (void) setLogoRect:(CGRect)logoRect{
  895. _logoRect = logoRect;
  896. [self setRect:logoRect ofLayer:_logoPicLayer];
  897. }
  898. // 水印logo的图片的透明度
  899. @synthesize logoAlpha = _logoAlpha;
  900. - (CGFloat)logoAlpha{
  901. return [_vPreviewMixer getPicAlphaOfLayer:_logoPicLayer];
  902. }
  903. - (void)setLogoAlpha:(CGFloat)alpha{
  904. [_vPreviewMixer setPicAlpha:alpha ofLayer:_logoPicLayer];
  905. [_vStreamMixer setPicAlpha:alpha ofLayer:_logoPicLayer];
  906. }
  907. // 水印文字的位置
  908. @synthesize textRect = _textRect;
  909. - (CGRect) textRect {
  910. return [_vPreviewMixer getPicRectOfLayer:_logoTxtLayer];
  911. }
  912. - (void) setTextRect:(CGRect)rect{
  913. _textRect = rect;
  914. [self setRect:rect ofLayer:_logoTxtLayer];
  915. }
  916. /**
  917. @abstract 刷新水印文字的内容
  918. @discussion 先修改文字的内容或格式,调用该方法后生效
  919. @see textLable
  920. */
  921. - (void) updateTextLabel{
  922. if ( [_textLabel.text length] <= 0 ){
  923. _textPic = nil;
  924. [_vPreviewMixer clearPicOfLayer:_logoPicLayer];
  925. [_vStreamMixer clearPicOfLayer:_logoPicLayer];
  926. return;
  927. }
  928. [_textLabel sizeToFit];
  929. UIImage * img = [self imageFromUIView:_textLabel];
  930. _textPic = [[GPUImagePicture alloc] initWithImage:img];
  931. [self addPic:_textPic ToMixerAt:_logoTxtLayer];
  932. [_textPic processImage];
  933. }
  934. @synthesize capturePixelFormat = _capturePixelFormat;
  935. - (void)setCapturePixelFormat: (OSType) fmt {
  936. if (_vCapDev.isRunning){
  937. return;
  938. }
  939. if(fmt != kCVPixelFormatType_32BGRA &&
  940. fmt != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange &&
  941. fmt != kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
  942. {
  943. fmt = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
  944. }
  945. _capturePixelFormat = fmt;
  946. _vCapDev.outputPixelFmt = fmt;
  947. _capToGpu =[[KSYGPUPicInput alloc] initWithFmt:fmt];
  948. [self setupVideoPath];
  949. [self updatePreDimension];
  950. }
  951. /// 设置gpu输出的图像像素格式
  952. @synthesize gpuOutputPixelFormat = _gpuOutputPixelFormat;
  953. - (void)setGpuOutputPixelFormat: (OSType) fmt {
  954. if ([_streamerBase isStreaming]){
  955. return;
  956. }
  957. if( fmt != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange &&
  958. fmt != kCVPixelFormatType_420YpCbCr8BiPlanarFullRange &&
  959. fmt != kCVPixelFormatType_420YpCbCr8PlanarFullRange &&
  960. fmt != kCVPixelFormatType_420YpCbCr8Planar ){
  961. fmt = kCVPixelFormatType_32BGRA;
  962. }
  963. _gpuOutputPixelFormat = fmt;
  964. _gpuToStr =[[KSYGPUPicOutput alloc] initWithOutFmt:fmt];
  965. [self setupVideoPath];
  966. [self updateStrDimension:self.videoOrientation];
  967. }
  968. #pragma mark - rotate & mirror
  969. - (void) setPreviewMirrored:(BOOL)bMirrored {
  970. if(_vPreviewMixer){
  971. GPUImageRotationMode ro = bMirrored ? kGPUImageFlipHorizonal: kGPUImageNoRotation;
  972. int ang = _previewRotateAng / M_PI_2;
  973. BOOL capAng = GPUImageRotationSwapsWidthAndHeight(_capToGpu.outputRotation);
  974. if ( !capAng && (ang == 1 || ang == 3)) {
  975. ro = bMirrored ? kGPUImageFlipVertical : kGPUImageNoRotation;
  976. }
  977. [_vPreviewMixer setPicRotation:ro ofLayer:_cameraLayer];
  978. }
  979. _previewMirrored = bMirrored;
  980. return ;
  981. }
  982. - (void) setStreamerMirrored:(BOOL)bMirrored {
  983. if (_vStreamMixer){
  984. GPUImageRotationMode ro = bMirrored ? kGPUImageFlipHorizonal: kGPUImageNoRotation;
  985. [_vStreamMixer setPicRotation:ro ofLayer:_cameraLayer];
  986. }
  987. _streamerMirrored = bMirrored;
  988. }
  989. - (void) setStreamerFreezed:(BOOL)streamerFreezed {
  990. _streamerFreezed = streamerFreezed;
  991. _gpuToStr.bAutoRepeat = streamerFreezed;
  992. }
  993. int UIOrienToIdx (UIInterfaceOrientation orien) {
  994. switch (orien) {
  995. case UIInterfaceOrientationPortrait:
  996. return 0;
  997. case UIInterfaceOrientationPortraitUpsideDown:
  998. return 1;
  999. case UIInterfaceOrientationLandscapeLeft:
  1000. return 2;
  1001. case UIInterfaceOrientationLandscapeRight:
  1002. return 3;
  1003. case UIInterfaceOrientationUnknown:
  1004. default:
  1005. return 0;
  1006. }
  1007. }
  1008. const static CGFloat KSYRotateAngles [4] [4] = {
  1009. M_PI_2*0,M_PI_2*2, M_PI_2*1, M_PI_2*3,
  1010. M_PI_2*2,M_PI_2*0, M_PI_2*3, M_PI_2*1,
  1011. M_PI_2*3,M_PI_2*1, M_PI_2*0, M_PI_2*2,
  1012. M_PI_2*1,M_PI_2*3, M_PI_2*2, M_PI_2*0,
  1013. };
  1014. - (void) setPreviewOrientation:(UIInterfaceOrientation)previewOrientation {
  1015. [self rotatePreviewTo:previewOrientation];
  1016. }
  1017. /**
  1018. @abstract 根据UI的朝向旋转预览视图, 保证预览视图全屏铺满窗口
  1019. @discussion 采集到的图像的朝向还是和启动时的朝向一致
  1020. */
  1021. - (void) rotatePreviewTo: (UIInterfaceOrientation) orie {
  1022. weakObj(self);
  1023. dispatch_async(dispatch_get_main_queue(), ^(){
  1024. [selfWeak internalRotatePreviewTo:orie];
  1025. });
  1026. }
  1027. -(void)internalRotatePreviewTo: (UIInterfaceOrientation) orie {
  1028. _previewOrientation = orie;
  1029. UIView* view = [_preview superview];
  1030. if (_videoOrientation == orie || view == nil) {
  1031. _previewRotateAng = 0;
  1032. }
  1033. else {
  1034. int capOri = UIOrienToIdx(_vCapDev.outputImageOrientation);
  1035. int appOri = UIOrienToIdx(orie);
  1036. _previewRotateAng = KSYRotateAngles[ capOri ][ appOri ];
  1037. }
  1038. for (UIView<GPUImageInput> *v in _vPreviewMixer.targets) {
  1039. v.transform = CGAffineTransformMakeRotation(_previewRotateAng);
  1040. }
  1041. _preview.frame = view.bounds;
  1042. [self setPreviewMirrored: _previewMirrored];
  1043. }
  1044. const static GPUImageRotationMode KSYRotateMode [4] [4] = {
  1045. kGPUImageNoRotation, kGPUImageRotate180,kGPUImageRotateRight, kGPUImageRotateLeft,
  1046. kGPUImageRotate180, kGPUImageNoRotation, kGPUImageRotateLeft, kGPUImageRotateRight,
  1047. kGPUImageRotateLeft,kGPUImageRotateRight, kGPUImageNoRotation, kGPUImageRotate180,
  1048. kGPUImageRotateRight, kGPUImageRotateLeft, kGPUImageRotate180, kGPUImageNoRotation,
  1049. };
  1050. -(void) setStreamOrientation:(UIInterfaceOrientation)streamOrientation {
  1051. [self rotateStreamTo:streamOrientation];
  1052. }
  1053. /**
  1054. @abstract 根据UI的朝向旋转推流画面
  1055. */
  1056. - (void) rotateStreamTo: (UIInterfaceOrientation) orie {
  1057. _streamOrientation = orie;
  1058. if (_gpuToStr.bAutoRepeat) {
  1059. return;
  1060. }
  1061. int capOri = UIOrienToIdx(_videoOrientation);
  1062. int appOri = UIOrienToIdx(orie);
  1063. GPUImageRotationMode oppositeMode = KSYRotateMode[appOri][capOri];
  1064. [_preview setInputRotation:oppositeMode atIndex:0];
  1065. [self updatePreDimension];
  1066. [self updateStrDimension:orie];
  1067. GPUImageRotationMode mode = KSYRotateMode[capOri][appOri];
  1068. _capToGpu.outputRotation = mode;
  1069. [self setStreamerMirrored: _streamerMirrored];
  1070. [self setPreviewMirrored: _previewMirrored];
  1071. }
  1072. /**
  1073. @abstract 摄像头自动曝光
  1074. */
  1075. - (BOOL)exposureAtPoint:(CGPoint )point{
  1076. AVCaptureDevice *dev = _vCapDev.inputCamera;
  1077. NSError *error;
  1078. if ([dev isExposurePointOfInterestSupported] && [dev isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
  1079. if ([dev lockForConfiguration:&error]) {
  1080. [dev setExposurePointOfInterest:point]; // 曝光点
  1081. [dev setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
  1082. [dev unlockForConfiguration];
  1083. return YES;
  1084. }
  1085. }
  1086. return NO;
  1087. }
  1088. /**
  1089. @abstract 摄像头自动变焦
  1090. */
  1091. - (BOOL)focusAtPoint:(CGPoint )point{
  1092. AVCaptureDevice *dev = _vCapDev.inputCamera;
  1093. NSError *error;
  1094. if ([dev isFocusPointOfInterestSupported] && [dev isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
  1095. if ([dev lockForConfiguration:&error]) {
  1096. [dev setFocusPointOfInterest:point];
  1097. [dev setFocusMode:AVCaptureFocusModeAutoFocus];
  1098. [dev unlockForConfiguration];
  1099. return YES;
  1100. }
  1101. }
  1102. return NO;
  1103. }
  1104. @synthesize pinchZoomFactor =_pinchZoomFactor;
  1105. - (CGFloat) pinchZoomFactor {
  1106. _pinchZoomFactor = _vCapDev.inputCamera.videoZoomFactor;
  1107. return _pinchZoomFactor;
  1108. }
  1109. //设置新的触摸缩放因子
  1110. - (void)setPinchZoomFactor:(CGFloat)zoomFactor{
  1111. AVCaptureDevice *captureDevice=_vCapDev.inputCamera;
  1112. NSError *error = nil;
  1113. [captureDevice lockForConfiguration:&error];
  1114. if (!error) {
  1115. CGFloat videoMaxZoomFactor = captureDevice.activeFormat.videoMaxZoomFactor;
  1116. if (zoomFactor < 1.0f)
  1117. zoomFactor = 1.0f;
  1118. if (zoomFactor > videoMaxZoomFactor)
  1119. zoomFactor = videoMaxZoomFactor;
  1120. [captureDevice rampToVideoZoomFactor:zoomFactor withRate:1.0];
  1121. captureDevice.videoZoomFactor = zoomFactor;
  1122. [captureDevice unlockForConfiguration];
  1123. }
  1124. }
  1125. //设置采集和推流配置参数
  1126. - (void)setStreamerProfile:(KSYStreamerProfile)profile{
  1127. _streamerBase.videoCodec = KSYVideoCodec_AUTO;
  1128. _streamerBase.audioCodec = KSYAudioCodec_AT_AAC;
  1129. _streamerBase.bwEstimateMode = KSYBWEstMode_Default;
  1130. _streamerBase.videoMinFPS = 10;
  1131. _streamerBase.videoMaxFPS = 25;
  1132. _streamerBase.videoEncodePerf = KSYVideoEncodePer_HighPerformance;
  1133. switch (profile) {
  1134. case KSYStreamerProfile_360p_auto:
  1135. _capPreset = AVCaptureSessionPreset640x480;
  1136. _previewDimension = CGSizeMake(640, 360);
  1137. _streamDimension = CGSizeMake(640, 360);
  1138. self.videoFPS = 15;
  1139. _streamerBase.videoMaxBitrate = 512;
  1140. _streamerBase.audiokBPS = 64;
  1141. break;
  1142. case KSYStreamerProfile_360p_1:
  1143. _capPreset = AVCaptureSessionPreset640x480;
  1144. _previewDimension = CGSizeMake(640, 360);
  1145. _streamDimension = CGSizeMake(640, 360);
  1146. self.videoFPS = 15;
  1147. _streamerBase.videoMaxBitrate = 512;
  1148. _streamerBase.audiokBPS = 64;
  1149. break;
  1150. case KSYStreamerProfile_360p_2:
  1151. _capPreset = AVCaptureSessionPresetiFrame960x540;
  1152. _previewDimension = CGSizeMake(960, 540);
  1153. _streamDimension = CGSizeMake(640, 360);
  1154. self.videoFPS = 15;
  1155. _streamerBase.videoMaxBitrate = 512;
  1156. _streamerBase.audiokBPS = 64;
  1157. break;
  1158. case KSYStreamerProfile_360p_3:
  1159. _capPreset = AVCaptureSessionPreset1280x720;
  1160. _previewDimension = CGSizeMake(1280, 720);
  1161. _streamDimension = CGSizeMake(640, 360);
  1162. self.videoFPS = 20;
  1163. _streamerBase.videoMaxBitrate = 768;
  1164. _streamerBase.audiokBPS = 64;
  1165. break;
  1166. case KSYStreamerProfile_540p_auto:
  1167. _capPreset = AVCaptureSessionPresetiFrame960x540;
  1168. _previewDimension = CGSizeMake(960, 540);
  1169. _streamDimension = CGSizeMake(960, 540);
  1170. self.videoFPS = 15;
  1171. _streamerBase.videoMaxBitrate = 768;
  1172. _streamerBase.audiokBPS = 64;
  1173. break;
  1174. case KSYStreamerProfile_540p_1:
  1175. _capPreset = AVCaptureSessionPresetiFrame960x540;
  1176. _previewDimension = CGSizeMake(960, 540);
  1177. _streamDimension = CGSizeMake(960, 540);
  1178. self.videoFPS = 15;
  1179. _streamerBase.videoMaxBitrate = 768;
  1180. _streamerBase.audiokBPS = 64;
  1181. break;
  1182. case KSYStreamerProfile_540p_2:
  1183. _capPreset = AVCaptureSessionPreset1280x720;
  1184. _previewDimension = CGSizeMake(1280, 720);
  1185. _streamDimension = CGSizeMake(960, 540);
  1186. self.videoFPS = 15;
  1187. _streamerBase.videoMaxBitrate = 768;
  1188. _streamerBase.audiokBPS = 64;
  1189. break;
  1190. case KSYStreamerProfile_540p_3:
  1191. _capPreset = AVCaptureSessionPreset1280x720;
  1192. _previewDimension = CGSizeMake(1280, 720);
  1193. _streamDimension = CGSizeMake(960, 540);
  1194. self.videoFPS = 20;
  1195. _streamerBase.videoMaxBitrate = 1024;
  1196. _streamerBase.audiokBPS = 64;
  1197. break;
  1198. case KSYStreamerProfile_720p_auto:
  1199. _capPreset = AVCaptureSessionPreset1280x720;
  1200. _previewDimension = CGSizeMake(1280, 720);
  1201. _streamDimension = CGSizeMake(1280, 720);
  1202. self.videoFPS = 15;
  1203. _streamerBase.videoMaxBitrate = 1024;
  1204. _streamerBase.audiokBPS = 128;
  1205. break;
  1206. case KSYStreamerProfile_720p_1:
  1207. _capPreset = AVCaptureSessionPreset1280x720;
  1208. _previewDimension = CGSizeMake(1280, 720);
  1209. _streamDimension = CGSizeMake(1280, 720);
  1210. self.videoFPS = 15;
  1211. _streamerBase.videoMaxBitrate = 1024;
  1212. _streamerBase.audiokBPS = 128;
  1213. break;
  1214. case KSYStreamerProfile_720p_2:
  1215. _capPreset = AVCaptureSessionPreset1280x720;
  1216. _previewDimension = CGSizeMake(1280, 720);
  1217. _streamDimension = CGSizeMake(1280, 720);
  1218. self.videoFPS = 20;
  1219. _streamerBase.videoMaxBitrate = 1280;
  1220. _streamerBase.audiokBPS = 128;
  1221. break;
  1222. case KSYStreamerProfile_720p_3:
  1223. _capPreset = AVCaptureSessionPreset1280x720;
  1224. _previewDimension = CGSizeMake(1280, 720);
  1225. _streamDimension = CGSizeMake(1280, 720);
  1226. self.videoFPS = 24;
  1227. _streamerBase.videoMaxBitrate = 1536;
  1228. _streamerBase.audiokBPS = 128;
  1229. break;
  1230. default:
  1231. NSLog(@"Set Invalid Profile");
  1232. return;
  1233. }
  1234. _streamerBase.videoInitBitrate = _streamerBase.videoMaxBitrate*6/10;
  1235. _streamerBase.videoMinBitrate = 0;
  1236. _streamerProfile = profile;
  1237. }
  1238. //
  1239. - (void)setAudioCaptureType:(KSYAudioCapType)audioCaptureType{
  1240. _audioCaptureType = audioCaptureType;
  1241. weakObj(self);
  1242. if (audioCaptureType == KSYAudioCap_AudioUnit) {
  1243. [_vCapDev removeAudioInputsAndOutputs];
  1244. if (!_aCapDev) {
  1245. _aCapDev = [[KSYAUAudioCapture alloc] init];
  1246. }
  1247. [_aCapDev startCapture];
  1248. _aCapDev.audioProcessingCallback = ^(CMSampleBufferRef buf){
  1249. if ( selfWeak.audioProcessingCallback ){
  1250. selfWeak.audioProcessingCallback(buf);
  1251. }
  1252. [selfWeak mixAudio:buf to:selfWeak.micTrack];
  1253. };
  1254. }else if (audioCaptureType == KSYAudioCap_AVCaptureDevice) {
  1255. _aCapDev = nil;
  1256. [_vCapDev addAudioInputsAndOutputs];
  1257. // 创建 dummy audio source
  1258. _dAudioSrc = [[KSYDummyAudioSource alloc] init];
  1259. _vCapDev.audioProcessingCallback = ^(CMSampleBufferRef buf){
  1260. if ( selfWeak.audioProcessingCallback ){
  1261. selfWeak.audioProcessingCallback(buf);
  1262. }
  1263. [selfWeak mixAudio:buf to:selfWeak.micTrack];
  1264. };
  1265. }
  1266. }
  1267. - (BOOL)setStabilizationMode:(AVCaptureVideoStabilizationMode)stabilizationMode{
  1268. _stabilizationMode = stabilizationMode;
  1269. __block BOOL supported = NO;
  1270. __weak typeof(self) weakSelf = self;
  1271. NSArray<AVCaptureOutput *> *outputs = _vCapDev.captureSession.outputs;
  1272. [outputs enumerateObjectsUsingBlock:^(AVCaptureOutput *obj, NSUInteger idx, BOOL * _Nonnull stop) {
  1273. if ([obj isKindOfClass:[AVCaptureVideoDataOutput class]]) {
  1274. if ([weakSelf.vCapDev.inputCamera.activeFormat isVideoStabilizationModeSupported:stabilizationMode]){
  1275. AVCaptureConnection *connection = [obj connectionWithMediaType:AVMediaTypeVideo];
  1276. if ([connection isVideoStabilizationSupported]) {
  1277. if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] != NSOrderedAscending) {
  1278. [connection setPreferredVideoStabilizationMode:stabilizationMode];
  1279. }else{
  1280. [connection setEnablesVideoStabilizationWhenAvailable:YES];
  1281. }
  1282. supported = YES;
  1283. }
  1284. }
  1285. }
  1286. }];
  1287. return supported;
  1288. }
  1289. @end