2018-04-10直播

寫文章

發(fā)現(xiàn)

關(guān)注

消息1

iOS視頻直播初窺:高仿<喵播APP>

?

Monkey_ALin?已關(guān)注

2016.07.06 00:20*?字數(shù) 3095?閱讀 69159評論 369喜歡 1174贊賞 9

效果圖

gif1

gif2

由于licecap錄制的GIF失幀太嚴(yán)重, 都模糊掉了, 再放兩張高清截圖

png1

png2

前言

今年三月份,斗魚獲騰訊領(lǐng)投的1億美元融資的消息被各大平臺報道轉(zhuǎn)載,在電競、泛娛樂已是熱門投資的當(dāng)下,網(wǎng)絡(luò)直播平臺自然也獲得了各界的關(guān)注。盜用兩張關(guān)于游戲直播的趨勢圖

游戲直播規(guī)模

游戲直播規(guī)模

這還僅僅是游戲直播這塊的蛋糕.直播行業(yè)的競爭會越來越激烈, 不管是主播還是直播平臺都面臨著激烈的競爭, 當(dāng)然直播行業(yè)也會越來越規(guī)范, 直播元素也越來越多.

視頻直播初窺

視頻直播,可以分為 采集,前處理,編碼,傳輸, 服務(wù)器處理,解碼,渲染

采集: iOS系統(tǒng)因為軟硬件種類不多, 硬件適配性比較好, 所以比較簡單. 而Android端市面上機型眾多, 要做些機型的適配工作.PC端是最麻煩的, 各種奇葩攝像頭驅(qū)動.所以現(xiàn)在很多的中小型直播平臺, 都放棄了PC的直播, 更有一些直播平臺只做iOS端的視頻直播.

前處理: 美顏算法,視頻的模糊效果, 水印等都是在這個環(huán)節(jié)做. 目前iOS端最著名開源框架的毫無疑問就是GPUImage.其中內(nèi)置了125種渲染效果, 還支持各種腳本自定義. 我高仿的喵播的美顏效果也是基于GPUImage的.

編碼: 重難點在于要在分辨率,幀率,碼率,GOP等參數(shù)設(shè)計上找到最佳平衡點。iOS8之后,?Apple開放了VideoToolbox.framework, 可以直接進行硬編解碼, 這也是為什么現(xiàn)在大多數(shù)直播平臺最低只支持到iOS8的原因之一. iOS端硬件兼容性比較好, 可以直接采取硬編碼. 而Android得硬編碼又是一大坑.

傳輸: 這塊一般都是交給CDN服務(wù)商.?CDN只提供帶寬和服務(wù)器之間的傳輸, 發(fā)送端和接收端的網(wǎng)絡(luò)連接抖動緩存還是要自己實現(xiàn)的.目前國內(nèi)最大的CDN服務(wù)商應(yīng)該是網(wǎng)宿.

服務(wù)器處理: 需要在服務(wù)器做一些流處理工作, 讓推送上來的流適配各個平臺各種不同的協(xié)議, 比如:RTMP,HLS,FLV...

解碼和渲染: 也就即音視頻的播放. 解碼毫無疑問也必須要硬解碼. iOS端兼容較好, Android依然大坑.這塊的難點在于音畫同步, 目前很多直播平臺這塊是硬傷.國內(nèi)比較好的開源項目應(yīng)該是B站開源的ijkplayer .?斗魚就是基于ijkplayer 的, 本項目也是基于ijkplayer 的.

技術(shù)坑 : 降噪, 音頻解碼器, 藍牙適配, 回聲消除, 信令控制, 登錄, 鑒權(quán), 權(quán)限管理, 狀態(tài)管理, 應(yīng)用消息, 消息推送, 禮物系統(tǒng), 即時聊天, 支付系統(tǒng), 統(tǒng)計系統(tǒng), 數(shù)據(jù)庫, 緩存, 分布式文件存儲, 消息隊列, 運維系統(tǒng)等等大小不一的坑等你來填!!!

資金坑 : 以帶寬為例, 2萬人同時在線, 手機碼率在600KB, 每個月的帶寬費用至少在30萬左右. 根據(jù)歡聚時代(YY)15年四季度財務(wù)報, 他們的帶寬成本為人民幣1.611億元, 折合每月5000萬+. 人力成本+渠道支出和其他支出就不詳談了.

社會坑: 還得每時每刻與各種黑暗勢力斗爭, 包括色情, 廣告, 刷小號, 刷充值, 告侵權(quán), DDos...(我反編譯喵播的官方APP, 他們的項目名就叫Shehui, O(∩_∩)O哈哈~)

項目下載地址

GitHub下載地址

前期準(zhǔn)備

項目主要是基于ijkplayer 的. 最好是打包成framework. 原本我準(zhǔn)備寫一個打包教程, 不過后來在簡書上發(fā)現(xiàn)了一篇特別詳細的打包blog, 分享給大家: http://www.itdecent.cn/p/1f06b27b3ac0.

如果你根據(jù)教程打包失敗了(當(dāng)然這種幾率比較小), 我這還有一份我已經(jīng)打包好的(Release版), 下載地址:

鏈接:http://pan.baidu.com/s/1eRVetdK?密碼:2dc0

下載后, 直接解壓即可.

項目文件結(jié)構(gòu)

Frameworks: 如果文件夾不存在, 點擊classes選擇Show in Finder, 新建一個即可, 將你打包的或者下載的framework拖入其中并拉進項目中. 你也可以自己建一個文件夾, 把這個Frameworks直接delete即可

Profile : 個人中心, 這里面只有一個ProfileController. 因為總寫重復(fù)代碼, 都寫吐了, 這兒有興趣的自己寫一下吧, So easy...

Network : 關(guān)于網(wǎng)絡(luò)連接的工具類. 關(guān)于網(wǎng)絡(luò)的實時監(jiān)控, 網(wǎng)絡(luò)狀態(tài)的切換, 網(wǎng)絡(luò)請求的工具類都在這里面.

Other : 全局的常量. 當(dāng)然你也可以在里面將文件結(jié)構(gòu)更加細化.

Home : 包含最新主播, 最熱直播, 關(guān)注的直播, 禮物排行榜等模塊. 還有最重要的視頻直播也在這里面了.

ShowTime :見名知意. 視頻直播的前處理, 智能美顏和H264硬編碼等都在這里面.

Main :?UITabBarController和UINavigationController的配置

Toos : 這兒命名有點不規(guī)范, 這里面放置的都是項目用到的分類

Login : 登錄模塊

Resource : 項目用到的資源文件

項目詳解

tip1: 判讀網(wǎng)絡(luò)類型.

在觀看直播的時候, 我們通常都是用WiFi或者3/4G(土豪級別的), 一般用戶在進行網(wǎng)絡(luò)切換的時候, 我們都要給出友善的提示, 告訴TA: 您的網(wǎng)絡(luò)狀態(tài)切換到了XX狀態(tài). 假設(shè)用戶從WiFi切換到4G, 你的應(yīng)用也沒個提醒, 導(dǎo)致TA的流量歸零甚至欠了運營商一屁股的錢, 我想你的APP的用戶體驗也就歸零或者為負了.

我們可以使用蘋果的Reachability結(jié)合下面的代碼實時監(jiān)聽網(wǎng)絡(luò)狀態(tài)的改變

typedefNS_ENUM(NSUInteger, NetworkStates) {? ? NetworkStatesNone,// 沒有網(wǎng)絡(luò)NetworkStates2G,// 2GNetworkStates3G,// 3GNetworkStates4G,// 4GNetworkStatesWIFI// WIFI};

// 判斷網(wǎng)絡(luò)類型+ (NetworkStates)getNetworkStates{NSArray*subviews = [[[[UIApplicationsharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];// 保存網(wǎng)絡(luò)狀態(tài)NetworkStates states = NetworkStatesNone;for(idchildinsubviews) {if([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {//獲取到狀態(tài)欄碼intnetworkType = [[child valueForKeyPath:@"dataNetworkType"] intValue];switch(networkType) {case0://無網(wǎng)模式states = NetworkStatesNone;break;case1:? ? ? ? ? ? ? ? ? ? states = NetworkStates2G;break;case2:? ? ? ? ? ? ? ? ? ? states = NetworkStates3G;break;case3:? ? ? ? ? ? ? ? ? ? states = NetworkStates4G;break;case5:? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? states = NetworkStatesWIFI;? ? ? ? ? ? ? ? }break;default:break;? ? ? ? ? ? }? ? ? ? }? ? }//根據(jù)狀態(tài)選擇returnstates;}

tip2: 登錄模塊

如果你多運行幾次就會發(fā)現(xiàn), 登錄模塊背景中播放的視頻是2個視頻每次隨機播放一個的.并且是無限重復(fù)的, 也就是說只要你一直呆著登錄界面, 就會單視頻循環(huán)播放當(dāng)前的視頻. 這兒的登錄只是幾個按鈕, 沒有具體的登錄邏輯, 隨便點哪一個按鈕都可以進入首頁.

我們需要監(jiān)聽視頻, 是否播放完成.

// 監(jiān)聽視頻是否播放完成[[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(didFinish) name:IJKMPMoviePlayerPlaybackDidFinishNotification object:nil];

如果播放完成了, 讓IJKFFMoviePlayerController再次play即可

- (void)didFinish{// 播放完之后, 繼續(xù)重播[self.player play];}

tip3: 首頁

首頁

這種效果相信很多人都看到過或者做過.我簡單說一下我的做法(不一定是最佳的, 只是提供一個思路)

一個父控制器HomeViewController+三個子控制器(最熱/最新/關(guān)注. 每個控制器各自管理自己的業(yè)務(wù)邏輯, 高內(nèi)聚低耦合). 重寫HomeViewController的loadView, 將self.view替換成UIScrollView. 將三個子控制器的view添加到UIScrollView上即可. 其他的效果實現(xiàn), 請參照我的代碼, 都有詳細的中文注釋.

tip4: 直播(面向觀眾端)

這個是整個項目的重點之一了.這種直播的布局, 應(yīng)該是比較主流的了. 我下載的好多直播類APP都是這個項目布局, 包括YY也是這種界面布局.這個里面涉及的東西比較多了, 三言兩語真說不清.

簡單說一下已經(jīng)實現(xiàn)的效果:

A: 主播的直播

B: 關(guān)聯(lián)主播的視頻直播, 默認是只有界面, 沒有聲音的. 點擊該視圖可以切換到此主播

C: 下拉切換另一個主播, 這個功能是很常見的. 做法是直播控制器是一個UICollectionViewController, 只有一個cell, 且cell.frame就是self.collectionViewb.bounds. 我們進入直播控制器的時候, 其實是傳進去一個關(guān)聯(lián)主播數(shù)組, 每次下拉的時候, 就加載數(shù)組里面的主播

D. 查看觀眾席的觀眾詳情

E. 查看主播詳情

F. 足跡: 粒子動畫, 后面詳解

G. 彈幕: 點擊最下方的工具欄第一個按鈕可以開啟/關(guān)閉彈幕, 后面詳解

...

tip5: 粒子動畫實現(xiàn)游客足跡

粒子動畫的layer是添加到播放器的view上面的. 下面代碼有詳細的注釋

CAEmitterLayer*emitterLayer = [CAEmitterLayerlayer];// 發(fā)射器在xy平面的中心位置emitterLayer.emitterPosition =CGPointMake(self.moviePlayer.view.frame.size.width-50,self.moviePlayer.view.frame.size.height-50);// 發(fā)射器的尺寸大小emitterLayer.emitterSize =CGSizeMake(20,20);// 渲染模式emitterLayer.renderMode = kCAEmitterLayerUnordered;// 開啟三維效果//? ? _emitterLayer.preservesDepth = YES;NSMutableArray*array = [NSMutableArrayarray];// 創(chuàng)建粒子for(inti =0; i<10; i++) {// 發(fā)射單元CAEmitterCell*stepCell = [CAEmitterCellemitterCell];// 粒子的創(chuàng)建速率,默認為1/sstepCell.birthRate =1;// 粒子存活時間stepCell.lifetime = arc4random_uniform(4) +1;// 粒子的生存時間容差stepCell.lifetimeRange =1.5;// 顏色// fire.color=[[UIColor colorWithRed:0.8 green:0.4 blue:0.2 alpha:0.1]CGColor];UIImage*image = [UIImageimageNamed:[NSStringstringWithFormat:@"good%d_30x30", i]];// 粒子顯示的內(nèi)容stepCell.contents = (id)[imageCGImage];// 粒子的名字//? ? ? ? ? ? [fire setName:@"step%d", i];// 粒子的運動速度stepCell.velocity = arc4random_uniform(100) +100;// 粒子速度的容差stepCell.velocityRange =80;// 粒子在xy平面的發(fā)射角度stepCell.emissionLongitude = M_PI+M_PI_2;;// 粒子發(fā)射角度的容差stepCell.emissionRange = M_PI_2/6;// 縮放比例stepCell.scale =0.3;? ? [array addObject:stepCell];}emitterLayer.emitterCells = array;[self.moviePlayer.view.layer insertSublayer:emitterLayer below:self.catEarView.layer];

tip6: 彈幕

彈幕使用的也是一個第三方輪子BarrageRenderer . 這個開源項目的文檔都是中文的, 用法也是很簡單的.

基本配置

_renderer = [[BarrageRenderer alloc] init];// 設(shè)置彈幕的顯示區(qū)域. 基于父控件的._renderer.canvasMargin =UIEdgeInsetsMake(ALinScreenHeight *0.3,10,10,10);[self.contentView addSubview:_renderer.view];

彈幕配置

#pragma mark - 彈幕描述符生產(chǎn)方法/// 生成精靈描述 - 過場文字彈幕- (BarrageDescriptor *)walkTextSpriteDescriptorWithDirection:(NSInteger)direction{? ? BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init];? ? descriptor.spriteName =NSStringFromClass([BarrageWalkTextSpriteclass]);? ? descriptor.params[@"text"] =self.danMuText[arc4random_uniform((uint32_t)self.danMuText.count)];? ? descriptor.params[@"textColor"] = Color(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256));? ? descriptor.params[@"speed"] = @(100* (double)random()/RAND_MAX+50);? ? descriptor.params[@"direction"] = @(direction);? ? descriptor.params[@"clickAction"] = ^{UIAlertView*alertView = [[UIAlertViewalloc]initWithTitle:@"提示"message:@"彈幕被點擊"delegate:nilcancelButtonTitle:@"取消"otherButtonTitles:nil];? ? ? ? [alertView show];? ? };returndescriptor;}

最后一步, 千萬要記得start

[_renderer start];

tip7: 智能美顏效果

現(xiàn)在的直播平臺, 美顏是標(biāo)配. 不然絕大多數(shù)的主播都是沒法看的.美顏算法需要用到GPU編程, 需要懂圖像處理的人. 圖像處理這一塊我不是很熟悉, 相關(guān)的文獻也是看得云里霧里的. 所以, 依然使用開源的輪子: GPUImage . 這個開源框架有近1.3W+star(7月5日數(shù)據(jù)), 真不是蓋的, 內(nèi)置125種濾鏡效果, 沒有你想不到, 只有你不會用. 我的項目中都有詳細的用法, 還是很簡單的. 在這里摘抄一份其.h文件的注釋. 一方面方便大家修改我項目中的美顏效果, 另一方面也是做個備份.(具體出處我真忘了, 如果有人找到了源地址鏈接, 可以聯(lián)系我加上)

#import"GLProgram.h"http:// Base classes#import"GPUImageOpenGLESContext.h"#import"GPUImageOutput.h"#import"GPUImageView.h"#import"GPUImageVideoCamera.h"#import"GPUImageStillCamera.h"#import"GPUImageMovie.h"#import"GPUImagePicture.h"#import"GPUImageRawDataInput.h"#import"GPUImageRawDataOutput.h"#import"GPUImageMovieWriter.h"#import"GPUImageFilterPipeline.h"#import"GPUImageTextureOutput.h"#import"GPUImageFilterGroup.h"#import"GPUImageTextureInput.h"#import"GPUImageUIElement.h"#import"GPUImageBuffer.h"http:// Filters#import"GPUImageFilter.h"#import"GPUImageTwoInputFilter.h"#pragmamark - 調(diào)整顏色 Handle Color#import"GPUImageBrightnessFilter.h"http://亮度#import"GPUImageExposureFilter.h"http://曝光#import"GPUImageContrastFilter.h"http://對比度#import"GPUImageSaturationFilter.h"http://飽和度#import"GPUImageGammaFilter.h"http://伽馬線#import"GPUImageColorInvertFilter.h"http://反色#import"GPUImageSepiaFilter.h"http://褐色(懷舊)#import"GPUImageLevelsFilter.h"http://色階#import"GPUImageGrayscaleFilter.h"http://灰度#import"GPUImageHistogramFilter.h"http://色彩直方圖,顯示在圖片上#import"GPUImageHistogramGenerator.h"http://色彩直方圖#import"GPUImageRGBFilter.h"http://RGB#import"GPUImageToneCurveFilter.h"http://色調(diào)曲線#import"GPUImageMonochromeFilter.h"http://單色#import"GPUImageOpacityFilter.h"http://不透明度#import"GPUImageHighlightShadowFilter.h"http://提亮陰影#import"GPUImageFalseColorFilter.h"http://色彩替換(替換亮部和暗部色彩)#import"GPUImageHueFilter.h"http://色度#import"GPUImageChromaKeyFilter.h"http://色度鍵#import"GPUImageWhiteBalanceFilter.h"http://白平橫#import"GPUImageAverageColor.h"http://像素平均色值#import"GPUImageSolidColorGenerator.h"http://純色#import"GPUImageLuminosity.h"http://亮度平均#import"GPUImageAverageLuminanceThresholdFilter.h"http://像素色值亮度平均,圖像黑白(有類似漫畫效果)#import"GPUImageLookupFilter.h"http://lookup 色彩調(diào)整#import"GPUImageAmatorkaFilter.h"http://Amatorka lookup#import"GPUImageMissEtikateFilter.h"http://MissEtikate lookup#import"GPUImageSoftEleganceFilter.h"http://SoftElegance lookup#pragmamark - 圖像處理 Handle Image#import"GPUImageCrosshairGenerator.h"http://十字#import"GPUImageLineGenerator.h"http://線條#import"GPUImageTransformFilter.h"http://形狀變化#import"GPUImageCropFilter.h"http://剪裁#import"GPUImageSharpenFilter.h"http://銳化#import"GPUImageUnsharpMaskFilter.h"http://反遮罩銳化#import"GPUImageFastBlurFilter.h"http://模糊#import"GPUImageGaussianBlurFilter.h"http://高斯模糊#import"GPUImageGaussianSelectiveBlurFilter.h"http://高斯模糊,選擇部分清晰#import"GPUImageBoxBlurFilter.h"http://盒狀模糊#import"GPUImageTiltShiftFilter.h"http://條紋模糊,中間清晰,上下兩端模糊#import"GPUImageMedianFilter.h"http://中間值,有種稍微模糊邊緣的效果#import"GPUImageBilateralFilter.h"http://雙邊模糊#import"GPUImageErosionFilter.h"http://侵蝕邊緣模糊,變黑白#import"GPUImageRGBErosionFilter.h"http://RGB侵蝕邊緣模糊,有色彩#import"GPUImageDilationFilter.h"http://擴展邊緣模糊,變黑白#import"GPUImageRGBDilationFilter.h"http://RGB擴展邊緣模糊,有色彩#import"GPUImageOpeningFilter.h"http://黑白色調(diào)模糊#import"GPUImageRGBOpeningFilter.h"http://彩色模糊#import"GPUImageClosingFilter.h"http://黑白色調(diào)模糊,暗色會被提亮#import"GPUImageRGBClosingFilter.h"http://彩色模糊,暗色會被提亮#import"GPUImageLanczosResamplingFilter.h"http://Lanczos重取樣,模糊效果#import"GPUImageNonMaximumSuppressionFilter.h"http://非最大抑制,只顯示亮度最高的像素,其他為黑#import"GPUImageThresholdedNonMaximumSuppressionFilter.h"http://與上相比,像素丟失更多#import"GPUImageSobelEdgeDetectionFilter.h"http://Sobel邊緣檢測算法(白邊,黑內(nèi)容,有點漫畫的反色效果)#import"GPUImageCannyEdgeDetectionFilter.h"http://Canny邊緣檢測算法(比上更強烈的黑白對比度)#import"GPUImageThresholdEdgeDetectionFilter.h"http://閾值邊緣檢測(效果與上差別不大)#import"GPUImagePrewittEdgeDetectionFilter.h"http://普瑞維特(Prewitt)邊緣檢測(效果與Sobel差不多,貌似更平滑)#import"GPUImageXYDerivativeFilter.h"http://XYDerivative邊緣檢測,畫面以藍色為主,綠色為邊緣,帶彩色#import"GPUImageHarrisCornerDetectionFilter.h"http://Harris角點檢測,會有綠色小十字顯示在圖片角點處#import"GPUImageNobleCornerDetectionFilter.h"http://Noble角點檢測,檢測點更多#import"GPUImageShiTomasiFeatureDetectionFilter.h"http://ShiTomasi角點檢測,與上差別不大#import"GPUImageMotionDetector.h"http://動作檢測#import"GPUImageHoughTransformLineDetector.h"http://線條檢測#import"GPUImageParallelCoordinateLineTransformFilter.h"http://平行線檢測#import"GPUImageLocalBinaryPatternFilter.h"http://圖像黑白化,并有大量噪點#import"GPUImageLowPassFilter.h"http://用于圖像加亮#import"GPUImageHighPassFilter.h"http://圖像低于某值時顯示為黑#pragmamark - 視覺效果 Visual Effect#import"GPUImageSketchFilter.h"http://素描#import"GPUImageThresholdSketchFilter.h"http://閥值素描,形成有噪點的素描#import"GPUImageToonFilter.h"http://卡通效果(黑色粗線描邊)#import"GPUImageSmoothToonFilter.h"http://相比上面的效果更細膩,上面是粗曠的畫風(fēng)#import"GPUImageKuwaharaFilter.h"http://桑原(Kuwahara)濾波,水粉畫的模糊效果;處理時間比較長,慎用#import"GPUImageMosaicFilter.h"http://黑白馬賽克#import"GPUImagePixellateFilter.h"http://像素化#import"GPUImagePolarPixellateFilter.h"http://同心圓像素化#import"GPUImageCrosshatchFilter.h"http://交叉線陰影,形成黑白網(wǎng)狀畫面#import"GPUImageColorPackingFilter.h"http://色彩丟失,模糊(類似監(jiān)控攝像效果)#import"GPUImageVignetteFilter.h"http://暈影,形成黑色圓形邊緣,突出中間圖像的效果#import"GPUImageSwirlFilter.h"http://漩渦,中間形成卷曲的畫面#import"GPUImageBulgeDistortionFilter.h"http://凸起失真,魚眼效果#import"GPUImagePinchDistortionFilter.h"http://收縮失真,凹面鏡#import"GPUImageStretchDistortionFilter.h"http://伸展失真,哈哈鏡#import"GPUImageGlassSphereFilter.h"http://水晶球效果#import"GPUImageSphereRefractionFilter.h"http://球形折射,圖形倒立#import"GPUImagePosterizeFilter.h"http://色調(diào)分離,形成噪點效果#import"GPUImageCGAColorspaceFilter.h"http://CGA色彩濾鏡,形成黑、淺藍、紫色塊的畫面#import"GPUImagePerlinNoiseFilter.h"http://柏林噪點,花邊噪點#import"GPUImage3x3ConvolutionFilter.h"http://3x3卷積,高亮大色塊變黑,加亮邊緣、線條等#import"GPUImageEmbossFilter.h"http://浮雕效果,帶有點3d的感覺#import"GPUImagePolkaDotFilter.h"http://像素圓點花樣#import"GPUImageHalftoneFilter.h"http://點染,圖像黑白化,由黑點構(gòu)成原圖的大致圖形#pragmamark - 混合模式 Blend#import"GPUImageMultiplyBlendFilter.h"http://通常用于創(chuàng)建陰影和深度效果#import"GPUImageNormalBlendFilter.h"http://正常#import"GPUImageAlphaBlendFilter.h"http://透明混合,通常用于在背景上應(yīng)用前景的透明度#import"GPUImageDissolveBlendFilter.h"http://溶解#import"GPUImageOverlayBlendFilter.h"http://疊加,通常用于創(chuàng)建陰影效果#import"GPUImageDarkenBlendFilter.h"http://加深混合,通常用于重疊類型#import"GPUImageLightenBlendFilter.h"http://減淡混合,通常用于重疊類型#import"GPUImageSourceOverBlendFilter.h"http://源混合#import"GPUImageColorBurnBlendFilter.h"http://色彩加深混合#import"GPUImageColorDodgeBlendFilter.h"http://色彩減淡混合#import"GPUImageScreenBlendFilter.h"http://屏幕包裹,通常用于創(chuàng)建亮點和鏡頭眩光#import"GPUImageExclusionBlendFilter.h"http://排除混合#import"GPUImageDifferenceBlendFilter.h"http://差異混合,通常用于創(chuàng)建更多變動的顏色#import"GPUImageSubtractBlendFilter.h"http://差值混合,通常用于創(chuàng)建兩個圖像之間的動畫變暗模糊效果#import"GPUImageHardLightBlendFilter.h"http://強光混合,通常用于創(chuàng)建陰影效果#import"GPUImageSoftLightBlendFilter.h"http://柔光混合#import"GPUImageChromaKeyBlendFilter.h"http://色度鍵混合#import"GPUImageMaskFilter.h"http://遮罩混合#import"GPUImageHazeFilter.h"http://朦朧加暗#import"GPUImageLuminanceThresholdFilter.h"http://亮度閾#import"GPUImageAdaptiveThresholdFilter.h"http://自適應(yīng)閾值#import"GPUImageAddBlendFilter.h"http://通常用于創(chuàng)建兩個圖像之間的動畫變亮模糊效果#import"GPUImageDivideBlendFilter.h"http://通常用于創(chuàng)建兩個圖像之間的動畫變暗模糊效果#pragmamark - 尚不清楚#import"GPUImageJFAVoroniFilter.h"#import"GPUImageVoroniConsumerFilter.h"

tip8: H264硬編碼

如果使用ijkplayer 使用硬解碼, 一句代碼即可.

// 開啟硬解碼[option setPlayerOptionValue:@"1"forKey:@"videotoolbox"];

硬編碼的應(yīng)用場景: 我們要將主播的視頻數(shù)據(jù)傳送給服務(wù)器

通過攝像頭來采集圖像,然后將采集到的圖像,通過硬編碼的方式進行編碼,最后編碼后的數(shù)據(jù)將其組合成H264的碼流通過網(wǎng)絡(luò)傳播。

攝像頭采集圖像, iOS系統(tǒng)提供了AVCaptureSession來采集攝像頭的圖像數(shù)據(jù). 項目中我是直接使用 GPUImage 中的GPUImageVideoCamera, 直接設(shè)置GPUImageVideoCamera的代理即可, 在其代理方法- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer;進行數(shù)據(jù)編碼即可.

切記一點: 不管是系統(tǒng)自帶的AVCaptureSession還是GPUImageVideoCamera采集到的數(shù)據(jù)都是未經(jīng)過編碼的CMSampleBuffer.

然后將采集到的數(shù)據(jù), 用iOS開放的VideoToolbox進行硬編碼. 關(guān)于VideoToolbox硬編解碼網(wǎng)上很多教程, 當(dāng)然最好是看Apple的官方文檔, 如果只是硬編碼, 看我的項目即可.

關(guān)鍵的編碼函數(shù)(來自YOLO直播負責(zé)人的開源項目 BeautifyFaceDemo )

voiddidCompressH264(void*outputCallbackRefCon,void*sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags,CMSampleBufferRefsampleBuffer ){if(status !=0)return;// 采集的未編碼數(shù)據(jù)是否準(zhǔn)備好if(!CMSampleBufferDataIsReady(sampleBuffer))? ? {NSLog(@"didCompressH264 data is not ready ");return;? ? }? ? ALinH264Encoder* encoder = (__bridge ALinH264Encoder*)outputCallbackRefCon;boolkeyframe = !CFDictionaryContainsKey((CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,true),0)), kCMSampleAttachmentKey_NotSync);if(keyframe)// 關(guān)鍵幀{CMFormatDescriptionRefformat =CMSampleBufferGetFormatDescription(sampleBuffer);? ? ? ? size_t sparameterSetSize, sparameterSetCount;constuint8_t *sparameterSet;? ? ? ? OSStatus statusCode =CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format,0, &sparameterSet, &sparameterSetSize, &sparameterSetCount,0);if(statusCode == noErr)? ? ? ? {? ? ? ? ? ? size_t pparameterSetSize, pparameterSetCount;constuint8_t *pparameterSet;? ? ? ? ? ? OSStatus statusCode =CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format,1, &pparameterSet, &pparameterSetSize, &pparameterSetCount,0);if(statusCode == noErr)? ? ? ? ? ? {? ? ? ? ? ? ? ? encoder->sps = [NSDatadataWithBytes:sparameterSet length:sparameterSetSize];? ? ? ? ? ? ? ? encoder->pps = [NSDatadataWithBytes:pparameterSet length:pparameterSetSize];NSLog(@"sps:%@ , pps:%@", encoder->sps, encoder->pps);? ? ? ? ? ? }? ? ? ? }? ? }CMBlockBufferRefdataBuffer =CMSampleBufferGetDataBuffer(sampleBuffer);? ? size_t length, totalLength;char*dataPointer;? ? OSStatus statusCodeRet =CMBlockBufferGetDataPointer(dataBuffer,0, &length, &totalLength, &dataPointer);if(statusCodeRet == noErr) {? ? ? ? ? ? ? ? size_t bufferOffset =0;staticconstintAVCCHeaderLength=4;while(bufferOffset < totalLength -AVCCHeaderLength)? ? ? ? {? ? ? ? ? ? uint32_t NALUnitLength =0;? ? ? ? ? ? memcpy(&NALUnitLength, dataPointer + bufferOffset,AVCCHeaderLength);? ? ? ? ? ? NALUnitLength =CFSwapInt32BigToHost(NALUnitLength);NSData*data = [[NSDataalloc] initWithBytes:(dataPointer + bufferOffset +AVCCHeaderLength) length:NALUnitLength];? ? ? ? ? ? bufferOffset +=AVCCHeaderLength+ NALUnitLength;NSLog(@"sendData-->> %@ %lu", data, bufferOffset);? ? ? ? }? ? ? ? ? ? }? ? }

感觸

雖說這個項目是個山寨的,?高仿的, 但是依然已經(jīng)很龐大了. 具體的細節(jié)還是需要大家自己去看我的項目源碼. 短短幾千字還真說不清這么多的知識點. blog的文章名字說了是初窺, 還真的只是初窺, 視頻直播里面的坑太多. 且行且珍惜...

tip: 本文理論知識部分, 采集自網(wǎng)絡(luò). 請記住一句話talk is cheap show me the code, 重點在于Demo項目本身. 理論部分我只是一個搬運工和總結(jié)者...

項目編譯環(huán)境

Xcode7(及以上)

最好是將項目跑在真機上. 有些地方模擬器是不支持的, 也看不到任何效果的, 比如硬編碼/智能美顏等, 這些功能模塊, 我做了限制的, 需要真機狀態(tài)才能進行.

項目下載地址

GitHub下載地址

請star和fork. 后續(xù)的bug會持續(xù)更新到github上的.

有問題可以在簡書給我留言/私信, 或者微博(簡書個人上首頁有我的微博)私信我.

7月9日凌晨更新: 項目已經(jīng)集成視頻直播推流

blog地址詳解快速集成iOS基于RTMP的視頻推流

聯(lián)系我

github

微博

簡書

小禮物走一走,來簡書關(guān)注我

贊賞支持

?等9人

?iOS

? 著作權(quán)歸作者所有

舉報文章

已關(guān)注Monkey_ALin

寫了 12770 字,被 3932 人關(guān)注,獲得了 2314 個喜歡

文能提筆控蘿莉,武能編碼調(diào)BUG

喜歡


1175

更多分享

369條評論?只看作者

按喜歡排序按時間正序按時間倒序


BB區(qū)塊鏈開發(fā)

47樓 · 2016.07.07 09:38

666

1人贊??回復(fù)


yangjianyin

71樓 · 2016.07.07 17:22

clang: error: linker command failed with exit code 1 (use -v to see invocation)

出現(xiàn)這個錯誤是為什么

1人贊??回復(fù)


moon_hj

151樓 · 2016.07.29 15:07

在網(wǎng)絡(luò)信號弱的情況下如何保證視頻質(zhì)量呢?直播開發(fā)技術(shù)交流群:183331015 請高手指教 謝謝

1人贊??回復(fù)

曾經(jīng)在奮斗

?可以做些處理,比如 一般CDN廠商提供的SDK都會稍作處理,ijkplayer的話,需要自己改。

https://github.com/daniulive/SmarterStreaming?可以看看這個

推送還好,播放的話,想做個好的播放器,還是很難,目前大多數(shù)開源改的直播播放器,延遲和穩(wěn)定性都控制的不太好

2017.04.28 09:51??回復(fù)

?添加新評論


goodfoapp

209樓 · 2016.09.11 17:34

贊博主大神!請大神 進群指導(dǎo)交流,直播開發(fā)技術(shù)交流群:183331015 謝謝 博主

1人贊??回復(fù)


蓬萊仙羽

251樓 · 2017.03.03 08:29

ld: library not found for -lAFNetworking

clang: error: linker command failed with exit code 1 (use -v to see invocation) 報錯

1人贊??回復(fù)


賤精先玍丶

258樓 · 2017.04.19 10:35

給我報的3個錯誤,歸類是Apple Mach-O Linker (Id) Error, 1."operator delete(void*)", referenced from: 2."operator new(unsigned long)", referenced from: 3.Linker command failed with exit code 1 (use -v to see invocation) 這3個

?我打包好,合并完模擬器和真機的framework啦...還是這樣

1人贊??回復(fù)

背鍋蝦

?解決了嗎,我也是遇到這情況

2017.08.02 10:59??回復(fù)

Zichoole

?按照說明,將Framework打包并復(fù)制到指定文件夾下之后,還需要在項目中做關(guān)聯(lián)才可以,在 Build Phases的Link Binary里添加指定的Framework~

2017.08.25 18:57??回復(fù)

文文文文西

?缺少這個依賴庫:libc++.tbd 在Link Binary加上就行了

2018.02.16 21:52??回復(fù)

?添加新評論


周克甲

2樓 · 2016.07.06 07:11

??

??回復(fù)


iOSWoden

3樓 · 2016.07.06 09:54

666

??回復(fù)


淳晨風(fēng)

4樓 · 2016.07.06 10:03

666

??回復(fù)


已刪號這名字都有人用

5樓 · 2016.07.06 10:04

666剛好公司要開始搞直播,幫助很大

??回復(fù)

我是賣報滴小行家

?@薛定諤的code?你們打算用第三方SDK嗎?好用么

2016.09.12 10:03??回復(fù)

已刪號這名字都有人用

?@一位農(nóng)民工?有的領(lǐng)導(dǎo)啥也不懂就是嫌貴,,,

2016.09.24 12:25??回復(fù)

前行的路上

?我們公司也開始寫直播,沒有接觸過,兩位大神寫的怎么樣了,給小弟點建議啊

2016.12.07 11:14??回復(fù)

?添加新評論?還有3條評論,?展開查看

1


2

3

4


下一頁

被以下專題收入,發(fā)現(xiàn)更多相似內(nèi)容

收入我的專題

面試題

iOS 開發(fā)?

首頁投稿(暫停...

????個人喜歡,收藏

iOS

iOS進階

iOS開發(fā)技巧

展開更多?

推薦閱讀更多精彩內(nèi)容

iOS:Touch ID簡易開發(fā)教程-仿alipay

效果圖 前言 2013年9月,蘋果為當(dāng)時發(fā)布的最新iPhone產(chǎn)品配備了一系列硬件升級方案。在iPhone 5s當(dāng)中,最具創(chuàng)新特性的機制無疑要數(shù)圍繞Home按鈕設(shè)計的超薄金屬圈,也就是被稱為Touch ID的指紋傳感器。這套Local Authentication框架能夠輕松實現(xiàn)用戶身份驗證,大家可以利用它來完成應(yīng)用程序的登錄機制或者通過它保護應(yīng)用程序當(dāng)中的敏感數(shù)據(jù)。 教程 1.導(dǎo)入對應(yīng)的框架頭文件 剛才我們說到,Touch ID指紋傳感器所屬Local Authentication框架.所以,第一步,我們需要導(dǎo)入頭文件 2.判斷設(shè)置是否支持Touch ID 或者 本機是否已經(jīng)錄入指紋 ...

?Monkey_ALin

iOS高仿:花田小憩3.0.1

前言 斷斷續(xù)續(xù)的已經(jīng)學(xué)習(xí)Swift一年多了, 從1.2到現(xiàn)在的2.2, 一直在語法之間徘徊, 學(xué)一段時間, 工作一忙, 再撿起來隔段時間又忘了.思來想去, 趁著這兩個月加班不是特別多, 就決定用swift仿寫一個完整項目. 個人文字功底有限, 就我而言, 這款A(yù)PP做的挺唯美的... github地址 github地址 聲明 此花田小憩項目里面的都是真實接口, 真實數(shù)據(jù), 僅供學(xué)習(xí), 毋作其他用途!!! 項目部分截圖 由于項目的大體功能都已經(jīng)實現(xiàn)了的, 所以整個項目還是比較龐大的.所以, 下面羅列部分功能的截圖.由于gif錄制的時候, 會重新渲染一遍圖片, 所以導(dǎo)致項目中用到高斯模糊的地...

?Monkey_ALin

【評論你的名字】為你寫三行情書

為你寫詩評論規(guī)則: 1. 只能評論名字,統(tǒng)一帶姓。 2. 我會回復(fù)屬于你名字的三行詩。 3. 都是原創(chuàng),如有類似,算我抄襲。 4. 涵養(yǎng)有限,作品不喜勿噴。 5. 精力有限,回復(fù)時間不定。 其他: 認同以上五則,期待留名。 只是一個人在寫,對我而言工作量會很巨大。 故回復(fù)期限無法保證,望諒解。 朋友們?nèi)缛粝埠靡部蓪懺娀貜?fù)評論名字。 不用私信問我,謝謝啦。

?小詩人阿耿

再見了,我的五線小城市!

今天我的選擇或許哪天也會成為你的選擇!——曉多 01 此刻,我坐在新的辦公室里,寬敞明亮,整理好一些交接的文件,加班完成了一篇材料,靜靜的坐下來喝了一口水。 打開電腦來寫這篇原本幾天就應(yīng)該動筆的文章,為自己曾經(jīng)的生活畫上一個句號,然后一切清零重新開始。 而幾天前我還在待了二十多年的小城市,官方的全國排名情況公布之后,這個我長大的豫北小城市已經(jīng)成了五線城市。 以上學(xué)為界,大學(xué)前的十多年在這里長大,這里不是我的祖籍卻是我的家。 畢業(yè)那年我回來過,在當(dāng)?shù)氐膱笊鐚嵙?xí),雖然沒有基本工資但僅靠稿費就已經(jīng)超過了不少記者,一張報紙有時會有五篇稿子是我寫的,經(jīng)常上頭版頭條。在報社招考的時候就知道能留下來,...

?曉多

《變形計》究竟給農(nóng)村孩子帶來了什么?

母親這是第一次看著節(jié)目哭了整場,稀里嘩啦的比平時我們和她發(fā)生爭執(zhí)的時候還動情。 頭一次主動叫我們老實坐在電視前,認真的觀看節(jié)目,我們有時候看的笑了,她還嚴(yán)肅的告訴我們不許笑!要體會不易。 01 《變形計》的故事結(jié)構(gòu)簡單,就是性格乖張的城市富二代和生活在邊遠山區(qū)家庭落魄的農(nóng)二代互換生活的故事。 再簡單不過的人設(shè)和最極致的環(huán)境設(shè)定,注定讓節(jié)目充滿著憤恨、憐惜、諒解、溫情和希望。 小時候把《變形計》當(dāng)娛樂節(jié)目看,總覺得正義上來了,想把城市小孩揍一圈,讓他不那么囂張;又看著農(nóng)村小孩對新世界的驚訝眼神,倍感憂傷。 我也是一個多愁善感的人,說我是個憤青也不過分。這種貧富差異巨大的交換生活,也顛覆了我...

?喜瓜樂樂

iOS視頻直播初窺:高仿<喵播APP>

https://github.com/ForIos/MiaoShow

?LLIOS

干貨 直播app源碼:高仿(喵播app)

視頻直播初窺:高仿<喵播APP> 轉(zhuǎn)載作者 Monkey_ALin一文 效果圖 會持續(xù)發(fā)布直播方面的資料 正在做直播的或?qū)χ辈ビ信d趣的可進直播交流群:183331015 共同學(xué)習(xí)探討 由于licecap錄制的GIF失幀太嚴(yán)重, 都模糊掉了, 再放兩張高清截圖 png1 前言...

?moon_hj

如何開發(fā)出一款仿映客直播APP項目實踐篇 -【原理篇】

前言:每個成功者多是站在巨人的肩膀上!在做直播開發(fā)時 碰到了很多問題,在收集了許多人博客的基礎(chǔ)上做出來了成功的直播項目并做了整理,并在最后奉上我的全部代碼。 其中采用博客的博主開篇在此感謝,本著開源分享的精神,我會將前輩的知識和自己開發(fā)中遇到的問題整理出完整的一套開發(fā)流程,...

?遠處山谷來的清風(fēng)

【如何快速的開發(fā)一個完整的iOS直播app】(原理篇)

目錄 【如何快速的開發(fā)一個完整的iOS直播app】(原理篇) 【如何快速的開發(fā)一個完整的iOS直播app】(播放篇) 【如何快速的開發(fā)一個完整的iOS直播app】(采集篇) 【如何快速的開發(fā)一個完整的iOS直播app】(美顏篇) 前言 大半年沒寫博客了,但我一直關(guān)注著互聯(lián)網(wǎng)...

?袁崢

【如何快速的開發(fā)一個完整的iOS直播app】(原理篇)

目錄 【如何快速的開發(fā)一個完整的iOS直播app】(原理篇) 【如何快速的開發(fā)一個完整的iOS直播app】(播放篇) 【如何快速的開發(fā)一個完整的iOS直播app】(采集篇) 【如何快速的開發(fā)一個完整的iOS直播app】(美顏篇) 前言 大半年沒寫博客了,但我一直關(guān)注著互聯(lián)網(wǎng)...

?小新xin

廢墟上的婚禮

“鈴……鈴…”凌晨兩點一陣急促的警鈴聲在三號消防站響起,不等鈴聲響第二遍,他已經(jīng)從床上騰將起來,大喝一聲“出警”,其他隊員一個個都打了個激靈,迅速地穿上隔熱服,蹬起消防膠靴,緊接著聽到腳步聲快速移動,忙而不亂,他一個箭步率先抱上滑桿,后面的隊員有序緊跟,幾乎是同一時刻同樣的...

?風(fēng)吹十月

豐碩人生——30天超級孵化營總結(jié)

1、30天的孵化營很快過去了,自己收獲多多,首先讓我看到系統(tǒng)的強大,也感受到了老師們的自律和付出,也感受到了我們系統(tǒng)情商高的人很多,也感受到了自己的不足. 2、這次超級孵化營的學(xué)習(xí),讓我學(xué)習(xí)到很多知識,每日干貨,快樂前進老師教的很細致,感受到老師時間規(guī)劃真好,效率真高,自己...

?豐碩人生

關(guān)于ScrollView上放滑動控件響應(yīng)問題

在項目開發(fā)時遇到一個問題,我在UIViewController上面直接創(chuàng)建了一個UIScrollView,把UIScrollerView作為一個子視圖添加到了UIViewController, 又再UIScrollerView中添加了一個UISlider的組件,在手勢滑動的...

?Johnny_Chang

那些痛苦讓我們成長

1. 這兩天和好友M聊天,她說,感覺壓力太大,快撐不住了。 注冊會計師聽起來光鮮亮麗有逼格,個種苦楚,也只有身在其中的人,才能感同身受。加班出差是常態(tài),年度審計更是高強度,高壓力,一個人哪怕化身三頭六臂,也是焦頭爛額。 我說,堅持一下,過了年審,就好些了。新的項目你第一次接...

?蝦米米

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 用到的組件 1、通過CocoaPods安裝 2、第三方類庫安裝 3、第三方服務(wù) 友盟社會化分享組件 友盟用戶反饋 ...
    SunnyLeong閱讀 15,139評論 1 180
  • 1.創(chuàng)建項目 第一步:在git或者碼云上創(chuàng)建項目例如項目名為testgit第二步:git clone https:...
    島在深海處閱讀 252評論 0 0
  • 斗米半升閱讀 370評論 0 1
  • 十一月九號就是我的生日了 還有三天 我知道自己在期待些什么 也知道自己不該期待什么 說不清道不明
    TinaFu閱讀 181評論 0 0
  • 一只沉默的巨獸,將一個閃閃發(fā)亮的女孩吞噬,嚼碎,幾經(jīng)揉搓、重塑之后,吐出一個人見人愛的賢妻良母,旁人則紛紛贊許并效...
    值完班不想疊被子閱讀 617評論 0 1

友情鏈接更多精彩內(nèi)容