ReplayKit2 有線投屏項目總結


一、實現目標

  iOS11.0以上設備通過USB線連接電腦,在電腦端實時看到手機屏幕內容

  畫質達到超清720級別,碼率可達到1Mbps以上

 

二、實現技術方案設計

 

  1、手機端采用ReplayKit2框架,在Upload Extension 進程中采集到屏幕內容YUV和系統聲音PCM+麥克風聲音PCM

  

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    switch (sampleBufferType) {
        case RPSampleBufferTypeVideo:
            break;
        case RPSampleBufferTypeAudioApp:
            break;
        case RPSampleBufferTypeAudioMic:
            break;
        default:
            break;
    }
}

  2、考慮在在Upload Extension 進程中或者主App進程中對圖像和聲音進行編碼,編碼成H264+AAC ,然后封裝為FLV格式的包,利用RTMP協議進行推流

    因為目前已經存在一套推流的接口,所以考慮在PC端增加RTMP收流服務,進行解析視頻流,然后渲染

 

  3、在PC端建立RTMP收流服務端,解碼,渲染;目前OBS已經存在相關模塊

 

三、遇到的問題以及解決方案

 

  1、如果在局域網中,目前的基礎上,無線推流到PC和推流到遠程直播服務器流程基本一樣

  2、如何規避局域網的網絡抖動環境,實現高清推流?局域網可能因為多人使用導致帶寬分配原因,以及信道干擾原因導致上行速率達不到標稱要求

    采用有線方案可以解決這個問題,那么手機如何利用USB線傳遞數據?

  3、USB傳遞有線數據有兩種方案:

    第一種是MIFI認證,使用iOS外設通信的庫,ExternalAccessory

    第二種是通過iproxy , 在PC端執行"iproxy pcport mobileport"的方式實現端口轉發,PC上連接pcport會連接到手機的mobileport,當一條TCP連接建立成功之后手機就可以利用USB線和PC實現雙向通信了

    這里為什么不能像安卓一樣,實現正向的轉發,將手機的端口轉發到PC上呢?這就是iOS系統相對封閉的原因;

    猜測安卓連接USB線的時候,PC端執行命令會在手機端出發操作實現端口轉發規則;而iOS不行

  那么最終采用的是第二種方案。

 

四、推流SDK協議改造

 

  對於采用的第二種方案,實施的時候遇到兩個問題?

  第一個如何實現由PC主動連接手機的過程,連接手機的哪個端口?

    對於這個問題,這里解決方案是,第一個在socket上面設置套接字為REUSE相關的屬性,保證端口能夠重復綁定成功,這里假定這個1397端口只有這個程序使用

                   第二個是在有線投屏的時候,手機要先掃碼得到PC的一個key,手機在啟動一個TCP監聽后將端口號聯系這個key一起發給我們的后台,后台通過push或者pc pull的方式,將這個信息通知到PC端,也就是建立信道的方式

 

  第二個問題,如何在一個RTMP.c的主動發起連接中,修改原有的方式,先嘗試被動連接(先啟動一個同步阻塞的監聽socket等待PC連接)。在這個邏輯中,因為等待過程是阻塞的,必然涉及到延時,在這里遇到了坑

    我們希望在 tcp socket bind一個端口,然后listen,然后accept的時候,希望在accept這個方法實現超時邏輯,最開始是這樣實現的

    

 int ret = ::setsockopt(m_nRealServerSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv));
        LOGW("socket accept start 1, set timeout ret = %d", ret);
        ret = ::setsockopt(m_nRealServerSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*) &tv, sizeof(tv));

    上述的代碼在安卓和PC上面生效,但是在iOS平台上面無效,雖然設置了一個超時時間,但是這個超時永遠不會觸發,accept永久阻塞

    為了規避這個問題,我采用select監聽文件描述符的方式,select跨平台兼容性效果更好

    采用以下代碼實現accept超時邏輯:

        int fd = -1;
        fd_set fdflag;
        sockaddr_in client_addr;
        memset(&client_addr, 0, sizeof(client_addr));
        
        
        FD_ZERO(&fdflag);
        FD_SET(m_nRealServerSocket, &fdflag);
            
        LOGW("socket accept start, timeout = %d secs", tv.tv_sec);
        bool hasProcessConnect = false;
        if(!hasProcessConnect && select(m_nRealServerSocket + 1, &fdflag, NULL, NULL, &tv) > 0)
        {
            hasProcessConnect = true;
            fd = accept(m_nRealServerSocket, (struct sockaddr*)NULL, NULL);
        }
        // 一次事件觸發之后, 清理監控的描述符
        FD_ZERO(&fdflag);

  

五、最終效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM