1. 程式人生 > >做一款仿映客的直播App?看我就夠了

做一款仿映客的直播App?看我就夠了

一、直播現狀簡介


Linkee.10

1.技術實現層面:

技術相對都比較成熟,裝置也都支援硬編碼。IOS還提供現成的 Video ToolBox框架,可以對攝像頭和流媒體資料結構進行處理,但Video ToolBox框架只相容8.0以上版本,8.0以下就需要用x264的庫軟編了。

github上有現成的開源實現,推流、美顏、水印、彈幕、點贊動畫、濾鏡、播放都有。技術其實不是很難,而且現在很多雲廠商都提供SDK,七牛雲、金山雲、樂視雲、騰訊雲、百度雲、鬥魚直播伴侶推流端,功能幾乎都是一樣的,沒啥亮點,不同的是整個直播平臺服務差異和接入的簡易性。後端現在 RTMP/HTTP-FLV 清一色,App掛個源站直接接入雲廠商或CDN就OK。

2.直播優化層面

其實最難的難點是提高首播時間、服務質量即Qos(Quality of Service,服務質量),如何在丟包率20%的情況下還能保障穩定、流暢的直播體驗,需要考慮以下方案:

1.為加快首播時間,收流伺服器主動推送 GOP :(Group of Pictures:策略影響編碼質量)所謂GOP,意思是畫面組,一個GOP就是一組連續的畫面至邊緣節點,邊緣節點快取 GOP,播放端則可以快速載入,減少回源延遲


GOP.1

2.GOP丟幀,為解決延時,為什麼會有延時,網路抖動、網路擁塞導致的資料傳送不出去,丟完之後所有的時間戳都要修改,切記,要不客戶端就會卡一個 GOP的時間,是由於 PTS(Presentation Time Stamp,PTS主要用於度量解碼後的視訊幀什麼時候被顯示出來) 和 DTS 的原因,或者播放器修正 DTS 和 PTS 也行(推流端丟GOD更復雜,丟 p 幀之前的 i 幀會花屏)


幀.2

3.純音訊丟幀,要解決音視訊不同步的問題,要讓視訊的 delta增量到你丟掉音訊的delta之後,再發音訊,要不就會音視訊不同步

4.源站主備切換和斷線重連

5.根據TCP擁塞視窗做智慧排程,當擁塞視窗過大說明節點服務質量不佳,需要切換節點和故障排查

6.增加上行、下行頻寬探測介面,當頻寬不滿足時降低視訊質量,即降低位元速率

7.定時獲取最優的推流、拉流鏈路IP,儘可能保證提供最好的服務

8.監控必須要,監控各個節點的Qos狀態,來做整個平臺的資源配置優化和排程


直播過程.3

9.如果產品從推流端、CDN、播放器都是自家的,保障 Qos 優勢非常大

10.當直播量非常大時,要加入叢集管理和排程,保障 Qos

11.播放端通過增加延時來減少網路抖動,通過快播來減少延時。(出自知乎宋少東)。

3.運營成本和客戶體驗

根據網上的資料,鬥魚 TV 為 3 億人民幣,戰旗 TV 為 1.5 億人民幣,龍珠為 1.2 億人民幣,虎牙為 3000 萬 + 人民幣。

運營和推廣:這個就比較燒錢了,一些做移動直播、遊戲直播、秀場直播的A輪至少得上千萬。

使用者體驗:流暢、不卡頓、不花屏、斷線重連、丟包策略、首畫載入速度、豐富的禮物系統,為了提高使用者體驗,可以在後臺載入其他頁面資料,但要在使用者體驗和記憶體優化方面找到平衡點。

二、流媒體傳輸

1.TCP:TCP為點對點的協議,雖然能保證了資料傳輸的可靠性,但是對伺服器資源耗費較大,在資料流大的場合難以保證資料流傳輸的實時性。

2.UDP:UDP為不可靠傳輸協議,不需要維護連線狀態,也不認為每個資料包都必須到達接受端,因此網路負荷比TCP小,傳輸速度也要比TCP快;但在網路越擁擠時,越有更多的資料包丟失。

3.RTMP:RTMP一個專門為高效傳輸視訊,音訊和資料而設計的協議。它通過建立一個二進位制TCP連線或者連線HTTP隧道實現實時的視訊和聲音傳輸。

4.FFmpeg:FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。採用LGPL或GPL許可證。它提供了錄製、轉換以及流化音視訊的完整解決方案


協議差異.4

三、專案搭建:採集端

1.目前比較知名的有VideoCore

目前國內很多知名的推流框架都是對VideoCore的二次開發。這個框架主要使用C++寫的,支援RTMP推流,但對於iOS開發者來說有點晦澀難懂(精通C++的除外)。想開源和免費的可以選擇現在的幾個知名專案VideoCore + GPUImage+基於GPU的美顏濾鏡 ,播放用IJKPlayer自己修改。

2.國內比較火的LiveVideoCoreSDK

框架提供IOS蘋果手機的RTMP推流填寫RTMP服務地址,直接就可以進行推流,SDK下載後簡單的工程配置後能直接執行,實現了美顏直播和濾鏡功能,基於OpenGL,前後攝像頭隨時切換,提供RTMP連線狀態的回撥。
這個框架是國內比較早的一款推流框架有不少在使用這個SDK,功能非常齊全,作者也比較牛,用來學習推流採集相關內容非常好,但是整合到工程中有些困難(對於我來說)。總的來說這是一款非常厲害的推流SDK,幾乎全部使用C++寫的,編譯效率非常好,如果有實力的話推薦使用這個框架來做自己專案的推流端。

3.可讀性比較好的推流LFLiveKit

框架支援RTMP(Real Time Messaging Protocol ):實時訊息傳輸協議,Adobe公司的。
HlS (HTTP Live Streaming) :蘋果自家的動態位元速率自適應技術。主要用於PC和Apple終端的音視訊服務。包括一個m3u(8)的索引檔案,TS媒體分片檔案和key加密串檔案。

推薦這個框架第一是因為它主要使用OC寫的,剩下的用C語言寫的,框架檔案十分清晰,這對不精通C++的初學者提供了很大的便利,並且拓展性非常強,支援動態切換位元速率功能,支援美顏功能。

4.美顏功能

美顏的話一般都是使用的GPUImage基於OpenGl開發,純OC語言,這個框架十分強大,可以做出各種不同濾鏡,可拓展性高。如果對美顏沒有具體思路可以直接用BeautifyFace,可以加入到專案中,很方便的實現美顏效果。

四、專案具體搭建

1.如何實現美顏?

美顏功能使用的是BeautifyFace,它可以很快速的實現美顏功能,效果不錯,它的底層還是基於的GPUImage,對GPUImage十分喜愛的Developer,可以參照BeautifyFace,寫出一個屬於自己的美顏功能,並且新增各種濾鏡。

2.懸浮TabBar的實現

這個TabBar看著像是用自定義TabBar做的,但事實上它還是用的系統的TabBar,給系統的tabBar.backgroundImage設定一張設計好的背景圖片。


TabBar.8

新增後會發現頂部有一條陰影線,並且TabBar的高度也不夠。陰影線與上圖綠色線條之間變成了透明顏色,實現下面方法隱藏陰影線,並且調高TabBar的高度。


TabBar.9
//隱藏陰影線
    [[UITabBar appearance] setShadowImage:[UIImage new]];
- (void)setupTabBarBackgroundImage {
    UIImage *image = [UIImage imageNamed:@"tab_bg"];

    CGFloat top = 40; // 頂端蓋高度
    CGFloat bottom = 40 ; // 底端蓋高度
    CGFloat left = 100; // 左端蓋寬度
    CGFloat right = 100; // 右端蓋寬度
    UIEdgeInsets insets = UIEdgeInsetsMake(top, left, bottom, right);
    // 指定為拉伸模式,伸縮後重新賦值
    UIImage *TabBgImage = [image resizableImageWithCapInsets:insets resizingMode:UIImageResizingModeStretch];
    self.tabBar.backgroundImage = TabBgImage;

    [[UITabBar appearance] setShadowImage:[UIImage new]];
    [[UITabBar appearance] setBackgroundImage:[[UIImage alloc]init]];
}

//自定義TabBar高度
- (void)viewWillLayoutSubviews {

    CGRect tabFrame = self.tabBar.frame;
    tabFrame.size.height = 60;
    tabFrame.origin.y = self.view.frame.size.height - 60;
    self.tabBar.frame = tabFrame;

}

3.播放端的實現

播放端用的針對RTMP優化過的ijkplayer(),ijkplayer是基於FFmpeg的跨平臺播放器,這個開源專案已經被多個 App 使用,其中映客、美拍和鬥魚使用了 ijkplayer(5700+️) 。在本文的末未提供了,已經打包好的ijkplayer,直接拖入專案就可以使用。省去了編譯的過程(編譯十分麻煩,並且容易出錯)。播放端呼叫ijkplayer參考VoideSun的簡仿映客所寫,如有關於ijkplayer的問題,可以請教這位大牛。
VoiderSun

- (void)goPlaying {

    //獲取url
    self.url = [NSURL URLWithString:_liveUrl];
    _player = [[IJKFFMoviePlayerController alloc] initWithContentURL:self.url withOptions:nil];

    UIView *playerview = [self.player view];
    UIView *displayView = [[UIView alloc] initWithFrame:self.view.bounds];

    self.PlayerView = displayView;
    [self.view addSubview:self.PlayerView];

    // 自動調整自己的寬度和高度
    playerview.frame = self.PlayerView.bounds;
    playerview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self.PlayerView insertSubview:playerview atIndex:1];
    [_player setScalingMode:IJKMPMovieScalingModeAspectFill];

}

4.推送端的實現

使用的是LFLiveKit,推流端可以選擇很多GitHub上的開源專案替代LFLiveKit,比如上面所提到的VideoCore,和LiveVideoCoreSDK。商用的話可以選擇各大廠商的SDK,網易直播雲、七牛、騰訊、百度、新浪、其中金山直播雲本人用過。使用直播雲的好處就是能快速上線App,功能十分齊全,可以播放器和推流端,伺服器一套下來,有專業客服人員幫助整合到工程中,缺點就是流量費太貴了,具體可以瞭解下各大廠商的收費標準。

- (UIButton*)startLiveButton{
    if(!_startLiveButton){

        _startLiveButton = [UIButton new];

        //位置
        _startLiveButton.frame = CGRectMake((XJScreenW - 200) * 0.5, XJScreenH - 100, 200, 40);

        _startLiveButton.layer.cornerRadius = _startLiveButton.frame.size.height * 0.5;
        [_startLiveButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [_startLiveButton.titleLabel setFont:[UIFont systemFontOfSize:16]];
        [_startLiveButton setTitle:@"開始直播" forState:UIControlStateNormal];
        [_startLiveButton setBackgroundColor:[UIColor grayColor]];
        _startLiveButton.exclusiveTouch = YES;
        __weak typeof(self) _self = self;
        [_startLiveButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
            _self.startLiveButton.selected = !_self.startLiveButton.selected;
            if(_self.startLiveButton.selected){
                [_self.startLiveButton setTitle:@"結束直播" forState:UIControlStateNormal];
                LFLiveStreamInfo *stream = [LFLiveStreamInfo new];
                stream.url = @"rtmp://daniulive.com:1935/live/stream238";
                [_self.session startLive:stream];
            }else{
                [_self.startLiveButton setTitle:@"開始直播" forState:UIControlStateNormal];
                [_self.session stopLive];
            }
        }];
    }
    return _startLiveButton;
}

stream.url是伺服器的地址,推流完成後使用VLC播放。別人家的伺服器,不建議推流時間過長(< 1分鐘),建議搭建自己的RTMP伺服器,詳情請見我的另一篇簡書文章。

給沒架設伺服器的朋友來一套福利:備用伺服器地址(親測可用):

  • rtmp://live.hkstv.hk.lxdns.com:1935/live/stream123

  • rtmp://live.hkstv.hk.lxdns.com:1935/live/hks
    (將hks名稱改成其他的,比如aaa)

  • rtmp://202.69.69.180:443/live/aaa
    (rtmp://202.69.69.180:443/webcast/bshdlive-pc)

  • rtmp://v1.one-tv.com:1935/live/aaa(rtmp://v1.one-tv.com:1935/live/mpegts.stream)

  • rtmp://203.207.99.19:1935/live/aaa(rtmp://203.207.99.19:1935/live/CCTV1)

  • rtmp://202.117.80.19:1935/live/aaa
    (rtmp://202.117.80.19:1935/live/live4)

  • rtmp://ams.studytv.cn/live/aaa
    (rtmp://ams.studytv.cn/livepkgr/264)

  • rtmp://60.174.36.89:1935/live/aaa
    (rtmp://60.174.36.89:1935/live/vod3)