iOS ReplayKit實時錄制屏幕實現方案的細節記錄


項目有個需求,需要把ios設備上的操作畫面實時傳輸出去,也就是類似推流手機直播畫面的方案。

一番調研后發現在ios中,我們可以通過ios自帶ReplayKit框架實現。

 

關於ReplayKit的講解,這篇文章寫的很好,可以看一下

iOS端使用replaykit錄制屏幕的技術細節

文章詳細介紹了ReplayKit的發展歷程,從ios9~ios12的每個版本的功能迭代都有寫,包括如何錄制當前app內容,還是制系統層次的內容等。

 

不過由於我的需求是只錄制當前App內容,所以下面只講解這方面的。

 

我的測試demo流程大概這樣

1、通過ReplayKit開啟錄屏

2、實時獲取視頻流CMSampleBuffer

3、對CMSampleBuffer處理發包或推流

 

為了效果快速呈現,這里我采取udp發包來傳輸內容

 

以下代碼僅供參考邏輯。

1、開啟錄屏

/// 開啟錄制屏幕
    func startRecord()  {
        
        if !RPScreenRecorder.shared().isAvailable{
            print("暫不支持xxx功能")
            return
        }
        
        if #available(iOS 11.0, *) {
            printDebug(message: "start record")
            if _udpSocket == nil{
          //初始化udp initUdp() connectUdp() queneConvertImage
= DispatchQueue(label: "teacher.show.quene") } isScreenRecording = true weak var weakself = self //該方法只能錄當前app,如果需要錄系統的,用broadcastxxx那個方法 RPScreenRecorder.shared().startCapture(handler: { (sampleBuffer, sampleBufferType, error) in if error == nil{ if CMSampleBufferDataIsReady(sampleBuffer) && sampleBufferType == RPSampleBufferType.video{ weakself?.queneConvertImage.async { weakself?.getUIImageFromCMSampleBuffer(sampleBuffer: sampleBuffer) } } }else{ printDebug(message: error.debugDescription) } }) { (finishError) in } } else { // Fallback on earlier versions print("xxx功能需要ios11版本及以上") } }

2、對視頻文件進行處理

func getUIImageFromCMSampleBuffer(sampleBuffer:CMSampleBuffer){
       
        /*
         關於兩種壓縮系數結果測試如下:
         UIImageJPEGRepresentation:
         0.01-63000  0.1-63000   0.2-67000  0.3-73000  0.4-85000  0.5-97000  0.6-110000  0.9-150000  1-290000
         UIImagePNGRepresentation:220000
         
         */
        
        let image1 = K12SampleBufferTool.image(from: sampleBuffer)
        if let data = UIImageJPEGRepresentation(image1!, 0.1),_udpSocket != nil{
            //兩次包相同,就忽略本次發送
            if lastBufferlen > 0 && fabs(Double(lastBufferlen - data.count)) < 100{
                return
            }
            lastBufferlen = data.count
            compressData(data: data, image: image1!)
            
        }
    }

    /// 壓縮圖片發送
    ///
    /// - Parameters:
    ///   - odata: <#odata description#>
    ///   - image: <#image description#>
    func compressData(data:Data,image:UIImage){
     //質量壓縮符合大小
if data.count < maxLength{ printDebug(message: "--- data:\(data) ") _udpSocket?.send(data, withTimeout: -1, tag: 0) return } let rate : Double = data.count > maxLength * 2 ? 1.0/Double(data.count/maxLength) : 0.5

     //采用size壓縮再次處理 if let d = UIImageJPEGRepresentation(image.compress(with: rate), 0.0),_udpSocket != nil{ //壓縮過還超過最大值,就不發送 if d.count > maxLength{ return } printDebug(message: "----data:\(data) ------compressdata.size:\(d)") _udpSocket?.send(d, withTimeout: -1, tag: 0) } }

 

buffer轉iamge的方法

+ (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer{
    
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CGImageRef image = NULL;
    if (@available(iOS 9.0, *)) {
        OSStatus createdImage = VTCreateCGImageFromCVPixelBuffer(imageBuffer, NULL, &image);
        UIImage * image1 = nil;
        if (createdImage == noErr) {
            image1 = [UIImage imageWithCGImage:image];
        }
        CGImageRelease(image);
        return image1;
    } else {
        return nil;
    }
   
}
View Code

 

以上是簡單的測試,udp發送這塊可以自行修改自己的邏輯。到這里,內容發出去了

 

3、停止錄制

/// 停止錄制屏幕
    func stopRecord() {
        if #available(iOS 11.0, *) {
            printDebug(message: "stop record")
            isScreenRecording = false
            RPScreenRecorder.shared().stopCapture { (error) in
                if error != nil{
                    printDebug(message: "stopRecord success")
                }
            }
        } else {
            // Fallback on earlier versions
        }
        
        //關閉udp
        nilSocket()
    }

 

結束之后記得調用一下關閉方法。

 

總結:

1、由於是采取圖片方式udp發送,在CMSampleBuffer轉image過程還是比較耗cpu的,但是,錄屏本事對cpu和內存對占用是極少的。

2、CMSampleBuffer的大小是根據畫面色彩度來的,如果畫面色彩很多,bytes會比較大。

3、如果不需要實時錄制,可以采用提供的結束統一獲取視頻的方式,那種更簡單。

4、用這個方法實時錄制需要ios11系統,這點是個硬傷;但關於另一個錄制系統方法,好像ios10就可以了,不過這種實現方式,用戶感知比較明顯,具體看上文的文章連接。

5、錄屏必須真機測試。

 

 


免責聲明!

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



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