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

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

一番調研後發如今ios中,咱們能夠經過ios自帶ReplayKit框架實現。app

 

關於ReplayKit的講解,這篇文章寫的很好,能夠看一下框架

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

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

 

不過因爲個人需求是隻錄製當前App內容,因此下面只講解這方面的。ide

 

個人測試demo流程大概這樣測試

一、經過ReplayKit開啓錄屏spa

二、實時獲取視頻流CMSampleBufferdebug

三、對CMSampleBuffer處理髮包或推流code

 

爲了效果快速呈現,這裏我採起udp發包來傳輸內容視頻

 

如下代碼僅供參考邏輯。

一、開啓錄屏

/// 開啓錄製屏幕
 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版本及以上") } }

二、對視頻文件進行處理

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發送這塊能夠自行修改本身的邏輯。到這裏,內容發出去了

 

三、中止錄製

/// 中止錄製屏幕
 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() }

 

結束以後記得調用一下關閉方法。

 

總結:

一、因爲是採起圖片方式udp發送,在CMSampleBuffer轉image過程仍是比較耗cpu的,可是,錄屏本事對cpu和內存對佔用是極少的。

二、CMSampleBuffer的大小是根據畫面色彩度來的,若是畫面色彩不少,bytes會比較大。

三、若是不須要實時錄製,能夠採用提供的結束統一獲取視頻的方式,那種更簡單。

四、用這個方法實時錄製須要ios11系統,這點是個硬傷;但關於另外一個錄製系統方法,好像ios10就能夠了,不過這種實現方式,用戶感知比較明顯,具體看上文的文章鏈接。

五、錄屏必須真機測試。

相關文章
相關標籤/搜索