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就能够了,不过这种实现方式,用户感知比较明显,具体看上文的文章链接。

五、录屏必须真机测试。

相关文章
相关标签/搜索