iOS視頻直播初窺:高仿<喵播APP>
?
2016.07.06 00:20*?字數 3095?閱讀 69159評論 369喜歡 1174贊賞 9
效果圖
gif1
gif2
由于licecap錄制的GIF失幀太嚴重, 都模糊掉了, 再放兩張高清截圖
png1
png2
前言
今年三月份,斗魚獲騰訊領投的1億美元融資的消息被各大平臺報道轉載,在電競、泛娛樂已是熱門投資的當下,網絡直播平臺自然也獲得了各界的關注。盜用兩張關于游戲直播的趨勢圖
游戲直播規模
游戲直播規模
這還僅僅是游戲直播這塊的蛋糕.直播行業的競爭會越來越激烈, 不管是主播還是直播平臺都面臨著激烈的競爭, 當然直播行業也會越來越規范, 直播元素也越來越多.
視頻直播初窺
視頻直播,可以分為 采集,前處理,編碼,傳輸, 服務器處理,解碼,渲染
采集: iOS系統因為軟硬件種類不多, 硬件適配性比較好, 所以比較簡單. 而Android端市面上機型眾多, 要做些機型的適配工作.PC端是最麻煩的, 各種奇葩攝像頭驅動.所以現在很多的中小型直播平臺, 都放棄了PC的直播, 更有一些直播平臺只做iOS端的視頻直播.
前處理: 美顏算法,視頻的模糊效果, 水印等都是在這個環節做. 目前iOS端最著名開源框架的毫無疑問就是GPUImage.其中內置了125種渲染效果, 還支持各種腳本自定義. 我高仿的喵播的美顏效果也是基于GPUImage的.
編碼: 重難點在于要在分辨率,幀率,碼率,GOP等參數設計上找到最佳平衡點。iOS8之后,?Apple開放了VideoToolbox.framework, 可以直接進行硬編解碼, 這也是為什么現在大多數直播平臺最低只支持到iOS8的原因之一. iOS端硬件兼容性比較好, 可以直接采取硬編碼. 而Android得硬編碼又是一大坑.
傳輸: 這塊一般都是交給CDN服務商.?CDN只提供帶寬和服務器之間的傳輸, 發送端和接收端的網絡連接抖動緩存還是要自己實現的.目前國內最大的CDN服務商應該是網宿.
服務器處理: 需要在服務器做一些流處理工作, 讓推送上來的流適配各個平臺各種不同的協議, 比如:RTMP,HLS,FLV...
解碼和渲染: 也就即音視頻的播放. 解碼毫無疑問也必須要硬解碼. iOS端兼容較好, Android依然大坑.這塊的難點在于音畫同步, 目前很多直播平臺這塊是硬傷.國內比較好的開源項目應該是B站開源的ijkplayer .?斗魚就是基于ijkplayer 的, 本項目也是基于ijkplayer 的.
技術坑 : 降噪, 音頻解碼器, 藍牙適配, 回聲消除, 信令控制, 登錄, 鑒權, 權限管理, 狀態管理, 應用消息, 消息推送, 禮物系統, 即時聊天, 支付系統, 統計系統, 數據庫, 緩存, 分布式文件存儲, 消息隊列, 運維系統等等大小不一的坑等你來填!!!
資金坑 : 以帶寬為例, 2萬人同時在線, 手機碼率在600KB, 每個月的帶寬費用至少在30萬左右. 根據歡聚時代(YY)15年四季度財務報, 他們的帶寬成本為人民幣1.611億元, 折合每月5000萬+. 人力成本+渠道支出和其他支出就不詳談了.
社會坑: 還得每時每刻與各種黑暗勢力斗爭, 包括色情, 廣告, 刷小號, 刷充值, 告侵權, DDos...(我反編譯喵播的官方APP, 他們的項目名就叫Shehui, O(∩_∩)O哈哈~)
項目下載地址
前期準備
項目主要是基于ijkplayer 的. 最好是打包成framework. 原本我準備寫一個打包教程, 不過后來在簡書上發現了一篇特別詳細的打包blog, 分享給大家: http://www.lxweimin.com/p/1f06b27b3ac0.
如果你根據教程打包失敗了(當然這種幾率比較小), 我這還有一份我已經打包好的(Release版), 下載地址:
鏈接:http://pan.baidu.com/s/1eRVetdK?密碼:2dc0
下載后, 直接解壓即可.
項目文件結構
Frameworks: 如果文件夾不存在, 點擊classes選擇Show in Finder, 新建一個即可, 將你打包的或者下載的framework拖入其中并拉進項目中. 你也可以自己建一個文件夾, 把這個Frameworks直接delete即可
Profile : 個人中心, 這里面只有一個ProfileController. 因為總寫重復代碼, 都寫吐了, 這兒有興趣的自己寫一下吧, So easy...
Network : 關于網絡連接的工具類. 關于網絡的實時監控, 網絡狀態的切換, 網絡請求的工具類都在這里面.
Other : 全局的常量. 當然你也可以在里面將文件結構更加細化.
Home : 包含最新主播, 最熱直播, 關注的直播, 禮物排行榜等模塊. 還有最重要的視頻直播也在這里面了.
ShowTime :見名知意. 視頻直播的前處理, 智能美顏和H264硬編碼等都在這里面.
Main :?UITabBarController和UINavigationController的配置
Toos : 這兒命名有點不規范, 這里面放置的都是項目用到的分類
Login : 登錄模塊
Resource : 項目用到的資源文件
項目詳解
tip1: 判讀網絡類型.
在觀看直播的時候, 我們通常都是用WiFi或者3/4G(土豪級別的), 一般用戶在進行網絡切換的時候, 我們都要給出友善的提示, 告訴TA: 您的網絡狀態切換到了XX狀態. 假設用戶從WiFi切換到4G, 你的應用也沒個提醒, 導致TA的流量歸零甚至欠了運營商一屁股的錢, 我想你的APP的用戶體驗也就歸零或者為負了.
我們可以使用蘋果的Reachability結合下面的代碼實時監聽網絡狀態的改變
typedefNS_ENUM(NSUInteger, NetworkStates) {? ? NetworkStatesNone,// 沒有網絡NetworkStates2G,// 2GNetworkStates3G,// 3GNetworkStates4G,// 4GNetworkStatesWIFI// WIFI};
// 判斷網絡類型+ (NetworkStates)getNetworkStates{NSArray*subviews = [[[[UIApplicationsharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];// 保存網絡狀態NetworkStates states = NetworkStatesNone;for(idchildinsubviews) {if([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {//獲取到狀態欄碼intnetworkType = [[child valueForKeyPath:@"dataNetworkType"] intValue];switch(networkType) {case0://無網模式states = NetworkStatesNone;break;case1:? ? ? ? ? ? ? ? ? ? states = NetworkStates2G;break;case2:? ? ? ? ? ? ? ? ? ? states = NetworkStates3G;break;case3:? ? ? ? ? ? ? ? ? ? states = NetworkStates4G;break;case5:? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? states = NetworkStatesWIFI;? ? ? ? ? ? ? ? }break;default:break;? ? ? ? ? ? }? ? ? ? }? ? }//根據狀態選擇returnstates;}
tip2: 登錄模塊
如果你多運行幾次就會發現, 登錄模塊背景中播放的視頻是2個視頻每次隨機播放一個的.并且是無限重復的, 也就是說只要你一直呆著登錄界面, 就會單視頻循環播放當前的視頻. 這兒的登錄只是幾個按鈕, 沒有具體的登錄邏輯, 隨便點哪一個按鈕都可以進入首頁.
我們需要監聽視頻, 是否播放完成.
// 監聽視頻是否播放完成[[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(didFinish) name:IJKMPMoviePlayerPlaybackDidFinishNotification object:nil];
如果播放完成了, 讓IJKFFMoviePlayerController再次play即可
- (void)didFinish{// 播放完之后, 繼續重播[self.player play];}
tip3: 首頁
首頁
這種效果相信很多人都看到過或者做過.我簡單說一下我的做法(不一定是最佳的, 只是提供一個思路)
一個父控制器HomeViewController+三個子控制器(最熱/最新/關注. 每個控制器各自管理自己的業務邏輯, 高內聚低耦合). 重寫HomeViewController的loadView, 將self.view替換成UIScrollView. 將三個子控制器的view添加到UIScrollView上即可. 其他的效果實現, 請參照我的代碼, 都有詳細的中文注釋.
tip4: 直播(面向觀眾端)
這個是整個項目的重點之一了.這種直播的布局, 應該是比較主流的了. 我下載的好多直播類APP都是這個項目布局, 包括YY也是這種界面布局.這個里面涉及的東西比較多了, 三言兩語真說不清.
簡單說一下已經實現的效果:
A: 主播的直播
B: 關聯主播的視頻直播, 默認是只有界面, 沒有聲音的. 點擊該視圖可以切換到此主播
C: 下拉切換另一個主播, 這個功能是很常見的. 做法是直播控制器是一個UICollectionViewController, 只有一個cell, 且cell.frame就是self.collectionViewb.bounds. 我們進入直播控制器的時候, 其實是傳進去一個關聯主播數組, 每次下拉的時候, 就加載數組里面的主播
D. 查看觀眾席的觀眾詳情
E. 查看主播詳情
F. 足跡: 粒子動畫, 后面詳解
G. 彈幕: 點擊最下方的工具欄第一個按鈕可以開啟/關閉彈幕, 后面詳解
...
tip5: 粒子動畫實現游客足跡
粒子動畫的layer是添加到播放器的view上面的. 下面代碼有詳細的注釋
CAEmitterLayer*emitterLayer = [CAEmitterLayerlayer];// 發射器在xy平面的中心位置emitterLayer.emitterPosition =CGPointMake(self.moviePlayer.view.frame.size.width-50,self.moviePlayer.view.frame.size.height-50);// 發射器的尺寸大小emitterLayer.emitterSize =CGSizeMake(20,20);// 渲染模式emitterLayer.renderMode = kCAEmitterLayerUnordered;// 開啟三維效果//? ? _emitterLayer.preservesDepth = YES;NSMutableArray*array = [NSMutableArrayarray];// 創建粒子for(inti =0; i<10; i++) {// 發射單元CAEmitterCell*stepCell = [CAEmitterCellemitterCell];// 粒子的創建速率,默認為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]];// 粒子顯示的內容stepCell.contents = (id)[imageCGImage];// 粒子的名字//? ? ? ? ? ? [fire setName:@"step%d", i];// 粒子的運動速度stepCell.velocity = arc4random_uniform(100) +100;// 粒子速度的容差stepCell.velocityRange =80;// 粒子在xy平面的發射角度stepCell.emissionLongitude = M_PI+M_PI_2;;// 粒子發射角度的容差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];// 設置彈幕的顯示區域. 基于父控件的._renderer.canvasMargin =UIEdgeInsetsMake(ALinScreenHeight *0.3,10,10,10);[self.contentView addSubview:_renderer.view];
彈幕配置
#pragma mark - 彈幕描述符生產方法/// 生成精靈描述 - 過場文字彈幕- (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: 智能美顏效果
現在的直播平臺, 美顏是標配. 不然絕大多數的主播都是沒法看的.美顏算法需要用到GPU編程, 需要懂圖像處理的人. 圖像處理這一塊我不是很熟悉, 相關的文獻也是看得云里霧里的. 所以, 依然使用開源的輪子: GPUImage . 這個開源框架有近1.3W+star(7月5日數據), 真不是蓋的, 內置125種濾鏡效果, 沒有你想不到, 只有你不會用. 我的項目中都有詳細的用法, 還是很簡單的. 在這里摘抄一份其.h文件的注釋. 一方面方便大家修改我項目中的美顏效果, 另一方面也是做個備份.(具體出處我真忘了, 如果有人找到了源地址鏈接, 可以聯系我加上)
#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 - 調整顏色 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://色調曲線#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 色彩調整#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://黑白色調模糊#import"GPUImageRGBOpeningFilter.h"http://彩色模糊#import"GPUImageClosingFilter.h"http://黑白色調模糊,暗色會被提亮#import"GPUImageRGBClosingFilter.h"http://彩色模糊,暗色會被提亮#import"GPUImageLanczosResamplingFilter.h"http://Lanczos重取樣,模糊效果#import"GPUImageNonMaximumSuppressionFilter.h"http://非最大抑制,只顯示亮度最高的像素,其他為黑#import"GPUImageThresholdedNonMaximumSuppressionFilter.h"http://與上相比,像素丟失更多#import"GPUImageSobelEdgeDetectionFilter.h"http://Sobel邊緣檢測算法(白邊,黑內容,有點漫畫的反色效果)#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://相比上面的效果更細膩,上面是粗曠的畫風#import"GPUImageKuwaharaFilter.h"http://桑原(Kuwahara)濾波,水粉畫的模糊效果;處理時間比較長,慎用#import"GPUImageMosaicFilter.h"http://黑白馬賽克#import"GPUImagePixellateFilter.h"http://像素化#import"GPUImagePolarPixellateFilter.h"http://同心圓像素化#import"GPUImageCrosshatchFilter.h"http://交叉線陰影,形成黑白網狀畫面#import"GPUImageColorPackingFilter.h"http://色彩丟失,模糊(類似監控攝像效果)#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://色調分離,形成噪點效果#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://點染,圖像黑白化,由黑點構成原圖的大致圖形#pragmamark - 混合模式 Blend#import"GPUImageMultiplyBlendFilter.h"http://通常用于創建陰影和深度效果#import"GPUImageNormalBlendFilter.h"http://正常#import"GPUImageAlphaBlendFilter.h"http://透明混合,通常用于在背景上應用前景的透明度#import"GPUImageDissolveBlendFilter.h"http://溶解#import"GPUImageOverlayBlendFilter.h"http://疊加,通常用于創建陰影效果#import"GPUImageDarkenBlendFilter.h"http://加深混合,通常用于重疊類型#import"GPUImageLightenBlendFilter.h"http://減淡混合,通常用于重疊類型#import"GPUImageSourceOverBlendFilter.h"http://源混合#import"GPUImageColorBurnBlendFilter.h"http://色彩加深混合#import"GPUImageColorDodgeBlendFilter.h"http://色彩減淡混合#import"GPUImageScreenBlendFilter.h"http://屏幕包裹,通常用于創建亮點和鏡頭眩光#import"GPUImageExclusionBlendFilter.h"http://排除混合#import"GPUImageDifferenceBlendFilter.h"http://差異混合,通常用于創建更多變動的顏色#import"GPUImageSubtractBlendFilter.h"http://差值混合,通常用于創建兩個圖像之間的動畫變暗模糊效果#import"GPUImageHardLightBlendFilter.h"http://強光混合,通常用于創建陰影效果#import"GPUImageSoftLightBlendFilter.h"http://柔光混合#import"GPUImageChromaKeyBlendFilter.h"http://色度鍵混合#import"GPUImageMaskFilter.h"http://遮罩混合#import"GPUImageHazeFilter.h"http://朦朧加暗#import"GPUImageLuminanceThresholdFilter.h"http://亮度閾#import"GPUImageAdaptiveThresholdFilter.h"http://自適應閾值#import"GPUImageAddBlendFilter.h"http://通常用于創建兩個圖像之間的動畫變亮模糊效果#import"GPUImageDivideBlendFilter.h"http://通常用于創建兩個圖像之間的動畫變暗模糊效果#pragmamark - 尚不清楚#import"GPUImageJFAVoroniFilter.h"#import"GPUImageVoroniConsumerFilter.h"
tip8: H264硬編碼
如果使用ijkplayer 使用硬解碼, 一句代碼即可.
// 開啟硬解碼[option setPlayerOptionValue:@"1"forKey:@"videotoolbox"];
硬編碼的應用場景: 我們要將主播的視頻數據傳送給服務器
通過攝像頭來采集圖像,然后將采集到的圖像,通過硬編碼的方式進行編碼,最后編碼后的數據將其組合成H264的碼流通過網絡傳播。
攝像頭采集圖像, iOS系統提供了AVCaptureSession來采集攝像頭的圖像數據. 項目中我是直接使用 GPUImage 中的GPUImageVideoCamera, 直接設置GPUImageVideoCamera的代理即可, 在其代理方法- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer;進行數據編碼即可.
切記一點: 不管是系統自帶的AVCaptureSession還是GPUImageVideoCamera采集到的數據都是未經過編碼的CMSampleBuffer.
然后將采集到的數據, 用iOS開放的VideoToolbox進行硬編碼. 關于VideoToolbox硬編解碼網上很多教程, 當然最好是看Apple的官方文檔, 如果只是硬編碼, 看我的項目即可.
關鍵的編碼函數(來自YOLO直播負責人的開源項目 BeautifyFaceDemo )
voiddidCompressH264(void*outputCallbackRefCon,void*sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags,CMSampleBufferRefsampleBuffer ){if(status !=0)return;// 采集的未編碼數據是否準備好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)// 關鍵幀{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);? ? ? ? }? ? ? ? ? ? }? ? }
感觸
雖說這個項目是個山寨的,?高仿的, 但是依然已經很龐大了. 具體的細節還是需要大家自己去看我的項目源碼. 短短幾千字還真說不清這么多的知識點. blog的文章名字說了是初窺, 還真的只是初窺, 視頻直播里面的坑太多. 且行且珍惜...
tip: 本文理論知識部分, 采集自網絡. 請記住一句話talk is cheap show me the code, 重點在于Demo項目本身. 理論部分我只是一個搬運工和總結者...
項目編譯環境
Xcode7(及以上)
最好是將項目跑在真機上. 有些地方模擬器是不支持的, 也看不到任何效果的, 比如硬編碼/智能美顏等, 這些功能模塊, 我做了限制的, 需要真機狀態才能進行.
項目下載地址
請star和fork. 后續的bug會持續更新到github上的.
有問題可以在簡書給我留言/私信, 或者微博(簡書個人上首頁有我的微博)私信我.
7月9日凌晨更新: 項目已經集成視頻直播推流
blog地址詳解快速集成iOS基于RTMP的視頻推流
聯系我
小禮物走一走,來簡書關注我
贊賞支持
?等9人
? 著作權歸作者所有
寫了 12770 字,被 3932 人關注,獲得了 2314 個喜歡
文能提筆控蘿莉,武能編碼調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)
出現這個錯誤是為什么
151樓 · 2016.07.29 15:07
在網絡信號弱的情況下如何保證視頻質量呢?直播開發技術交流群:183331015 請高手指教 謝謝
?可以做些處理,比如 一般CDN廠商提供的SDK都會稍作處理,ijkplayer的話,需要自己改。
https://github.com/daniulive/SmarterStreaming?可以看看這個
推送還好,播放的話,想做個好的播放器,還是很難,目前大多數開源改的直播播放器,延遲和穩定性都控制的不太好
2017.04.28 09:51??回復
209樓 · 2016.09.11 17:34
贊博主大神!請大神 進群指導交流,直播開發技術交流群: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??回復
?按照說明,將Framework打包并復制到指定文件夾下之后,還需要在項目中做關聯才可以,在 Build Phases的Link Binary里添加指定的Framework~
2017.08.25 18:57??回復
?缺少這個依賴庫:libc++.tbd 在Link Binary加上就行了
2018.02.16 21:52??回復
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??回復
?@一位農民工?有的領導啥也不懂就是嫌貴,,,
2016.09.24 12:25??回復
?我們公司也開始寫直播,沒有接觸過,兩位大神寫的怎么樣了,給小弟點建議啊
2016.12.07 11:14??回復
被以下專題收入,發現更多相似內容
面試題
iOS 開發?
首頁投稿(暫停...
????個人喜歡,收藏
iOS
iOS進階
iOS開發技巧
推薦閱讀更多精彩內容
效果圖 前言 2013年9月,蘋果為當時發布的最新iPhone產品配備了一系列硬件升級方案。在iPhone 5s當中,最具創新特性的機制無疑要數圍繞Home按鈕設計的超薄金屬圈,也就是被稱為Touch ID的指紋傳感器。這套Local Authentication框架能夠輕松實現用戶身份驗證,大家可以利用它來完成應用程序的登錄機制或者通過它保護應用程序當中的敏感數據。 教程 1.導入對應的框架頭文件 剛才我們說到,Touch ID指紋傳感器所屬Local Authentication框架.所以,第一步,我們需要導入頭文件 2.判斷設置是否支持Touch ID 或者 本機是否已經錄入指紋 ...
前言 斷斷續續的已經學習Swift一年多了, 從1.2到現在的2.2, 一直在語法之間徘徊, 學一段時間, 工作一忙, 再撿起來隔段時間又忘了.思來想去, 趁著這兩個月加班不是特別多, 就決定用swift仿寫一個完整項目. 個人文字功底有限, 就我而言, 這款APP做的挺唯美的... github地址 github地址 聲明 此花田小憩項目里面的都是真實接口, 真實數據, 僅供學習, 毋作其他用途!!! 項目部分截圖 由于項目的大體功能都已經實現了的, 所以整個項目還是比較龐大的.所以, 下面羅列部分功能的截圖.由于gif錄制的時候, 會重新渲染一遍圖片, 所以導致項目中用到高斯模糊的地...
為你寫詩評論規則: 1. 只能評論名字,統一帶姓。 2. 我會回復屬于你名字的三行詩。 3. 都是原創,如有類似,算我抄襲。 4. 涵養有限,作品不喜勿噴。 5. 精力有限,回復時間不定。 其他: 認同以上五則,期待留名。 只是一個人在寫,對我而言工作量會很巨大。 故回復期限無法保證,望諒解。 朋友們如若喜好也可寫詩回復評論名字。 不用私信問我,謝謝啦。
今天我的選擇或許哪天也會成為你的選擇!——曉多 01 此刻,我坐在新的辦公室里,寬敞明亮,整理好一些交接的文件,加班完成了一篇材料,靜靜的坐下來喝了一口水。 打開電腦來寫這篇原本幾天就應該動筆的文章,為自己曾經的生活畫上一個句號,然后一切清零重新開始。 而幾天前我還在待了二十多年的小城市,官方的全國排名情況公布之后,這個我長大的豫北小城市已經成了五線城市。 以上學為界,大學前的十多年在這里長大,這里不是我的祖籍卻是我的家。 畢業那年我回來過,在當地的報社實習,雖然沒有基本工資但僅靠稿費就已經超過了不少記者,一張報紙有時會有五篇稿子是我寫的,經常上頭版頭條。在報社招考的時候就知道能留下來,...
母親這是第一次看著節目哭了整場,稀里嘩啦的比平時我們和她發生爭執的時候還動情。 頭一次主動叫我們老實坐在電視前,認真的觀看節目,我們有時候看的笑了,她還嚴肅的告訴我們不許笑!要體會不易。 01 《變形計》的故事結構簡單,就是性格乖張的城市富二代和生活在邊遠山區家庭落魄的農二代互換生活的故事。 再簡單不過的人設和最極致的環境設定,注定讓節目充滿著憤恨、憐惜、諒解、溫情和希望。 小時候把《變形計》當娛樂節目看,總覺得正義上來了,想把城市小孩揍一圈,讓他不那么囂張;又看著農村小孩對新世界的驚訝眼神,倍感憂傷。 我也是一個多愁善感的人,說我是個憤青也不過分。這種貧富差異巨大的交換生活,也顛覆了我...
https://github.com/ForIos/MiaoShow
?LLIOS
視頻直播初窺:高仿<喵播APP> 轉載作者 Monkey_ALin一文 效果圖 會持續發布直播方面的資料 正在做直播的或對直播有興趣的可進直播交流群:183331015 共同學習探討 由于licecap錄制的GIF失幀太嚴重, 都模糊掉了, 再放兩張高清截圖 png1 前言...
?moon_hj
前言:每個成功者多是站在巨人的肩膀上!在做直播開發時 碰到了很多問題,在收集了許多人博客的基礎上做出來了成功的直播項目并做了整理,并在最后奉上我的全部代碼。 其中采用博客的博主開篇在此感謝,本著開源分享的精神,我會將前輩的知識和自己開發中遇到的問題整理出完整的一套開發流程,...
?遠處山谷來的清風
目錄 【如何快速的開發一個完整的iOS直播app】(原理篇) 【如何快速的開發一個完整的iOS直播app】(播放篇) 【如何快速的開發一個完整的iOS直播app】(采集篇) 【如何快速的開發一個完整的iOS直播app】(美顏篇) 前言 大半年沒寫博客了,但我一直關注著互聯網...
?袁崢
目錄 【如何快速的開發一個完整的iOS直播app】(原理篇) 【如何快速的開發一個完整的iOS直播app】(播放篇) 【如何快速的開發一個完整的iOS直播app】(采集篇) 【如何快速的開發一個完整的iOS直播app】(美顏篇) 前言 大半年沒寫博客了,但我一直關注著互聯網...
?小新xin
“鈴……鈴…”凌晨兩點一陣急促的警鈴聲在三號消防站響起,不等鈴聲響第二遍,他已經從床上騰將起來,大喝一聲“出警”,其他隊員一個個都打了個激靈,迅速地穿上隔熱服,蹬起消防膠靴,緊接著聽到腳步聲快速移動,忙而不亂,他一個箭步率先抱上滑桿,后面的隊員有序緊跟,幾乎是同一時刻同樣的...
?風吹十月
1、30天的孵化營很快過去了,自己收獲多多,首先讓我看到系統的強大,也感受到了老師們的自律和付出,也感受到了我們系統情商高的人很多,也感受到了自己的不足. 2、這次超級孵化營的學習,讓我學習到很多知識,每日干貨,快樂前進老師教的很細致,感受到老師時間規劃真好,效率真高,自己...
?豐碩人生
在項目開發時遇到一個問題,我在UIViewController上面直接創建了一個UIScrollView,把UIScrollerView作為一個子視圖添加到了UIViewController, 又再UIScrollerView中添加了一個UISlider的組件,在手勢滑動的...
?Johnny_Chang
1. 這兩天和好友M聊天,她說,感覺壓力太大,快撐不住了。 注冊會計師聽起來光鮮亮麗有逼格,個種苦楚,也只有身在其中的人,才能感同身受。加班出差是常態,年度審計更是高強度,高壓力,一個人哪怕化身三頭六臂,也是焦頭爛額。 我說,堅持一下,過了年審,就好些了。新的項目你第一次接...
?蝦米米