iOS視頻直播初窺:高仿<喵播APP>
?
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哈哈~)
項目下載地址
前期準(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)才能進行.
項目下載地址
請star和fork. 后續(xù)的bug會持續(xù)更新到github上的.
有問題可以在簡書給我留言/私信, 或者微博(簡書個人上首頁有我的微博)私信我.
7月9日凌晨更新: 項目已經(jīng)集成視頻直播推流
blog地址詳解快速集成iOS基于RTMP的視頻推流
聯(lián)系我
小禮物走一走,來簡書關(guān)注我
贊賞支持
?等9人
? 著作權(quán)歸作者所有
寫了 12770 字,被 3932 人關(guān)注,獲得了 2314 個喜歡
文能提筆控蘿莉,武能編碼調(diào)BUG
369條評論?只看作者
47樓 · 2016.07.07 09:38
666
71樓 · 2016.07.07 17:22
clang: error: linker command failed with exit code 1 (use -v to see invocation)
出現(xiàn)這個錯誤是為什么
151樓 · 2016.07.29 15:07
在網(wǎng)絡(luò)信號弱的情況下如何保證視頻質(zhì)量呢?直播開發(fā)技術(shù)交流群:183331015 請高手指教 謝謝
?可以做些處理,比如 一般CDN廠商提供的SDK都會稍作處理,ijkplayer的話,需要自己改。
https://github.com/daniulive/SmarterStreaming?可以看看這個
推送還好,播放的話,想做個好的播放器,還是很難,目前大多數(shù)開源改的直播播放器,延遲和穩(wěn)定性都控制的不太好
2017.04.28 09:51??回復(fù)
209樓 · 2016.09.11 17:34
贊博主大神!請大神 進群指導(dǎo)交流,直播開發(fā)技術(shù)交流群:183331015 謝謝 博主
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) 報錯
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啦...還是這樣
背鍋蝦:
?解決了嗎,我也是遇到這情況
2017.08.02 10:59??回復(fù)
?按照說明,將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
贊
??
3樓 · 2016.07.06 09:54
666
4樓 · 2016.07.06 10:03
666
5樓 · 2016.07.06 10:04
666剛好公司要開始搞直播,幫助很大
?@薛定諤的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ù)
被以下專題收入,發(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)錄入指紋 ...
前言 斷斷續(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)致項目中用到高斯模糊的地...
為你寫詩評論規(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)常上頭版頭條。在報社招考的時候就知道能留下來,...
母親這是第一次看著節(jié)目哭了整場,稀里嘩啦的比平時我們和她發(fā)生爭執(zhí)的時候還動情。 頭一次主動叫我們老實坐在電視前,認真的觀看節(jié)目,我們有時候看的笑了,她還嚴(yán)肅的告訴我們不許笑!要體會不易。 01 《變形計》的故事結(jié)構(gòu)簡單,就是性格乖張的城市富二代和生活在邊遠山區(qū)家庭落魄的農(nóng)二代互換生活的故事。 再簡單不過的人設(shè)和最極致的環(huán)境設(shè)定,注定讓節(jié)目充滿著憤恨、憐惜、諒解、溫情和希望。 小時候把《變形計》當(dāng)娛樂節(jié)目看,總覺得正義上來了,想把城市小孩揍一圈,讓他不那么囂張;又看著農(nóng)村小孩對新世界的驚訝眼神,倍感憂傷。 我也是一個多愁善感的人,說我是個憤青也不過分。這種貧富差異巨大的交換生活,也顛覆了我...
https://github.com/ForIos/MiaoShow
?LLIOS

視頻直播初窺:高仿<喵播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)吹十月

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),年度審計更是高強度,高壓力,一個人哪怕化身三頭六臂,也是焦頭爛額。 我說,堅持一下,過了年審,就好些了。新的項目你第一次接...
?蝦米米