使用从 [UInt8] 数据创建的 CGImage 频繁绘制 CGContext 时,iOS 应用程序崩溃

问题描述

现在我正在该模块中开发一个模块,我需要从数组 CGImage 创建视频,在执行此操作时,我的应用程序在某些时候崩溃了,我无法找出崩溃背后的确切原因。

任何人都可以建议我是否朝着正确的方向前进,我应该将 [CGImage] 转换为视频还是需要选择其他方法。

我也尝试将 CGImage 转换为 UIImage 并尝试创建视频,但仍然面临同样的问题。

我在 [UInt8] 数据中获取图像数据,那么转换图像格式和创建视频的正确方法是什么?

为了从 [CGImage] 创建视频,请遵循以下方法。 我正在使用 CGDataProvider 将 [UInt8] 数据转换为 CGImage,并将 CGImage 转换为 UIImage。我有一系列图像并收集 UIImage,然后合并图像并创建视频。

这是我从数据转换 CGImage 的代码。

private(set) var data: [UInt8]

var cgImage: CGImage? {

        let colorSpaceRef = CGColorSpaceCreateDeviceRGB()

        let bitsPerComponent = 8
        let bitsPerPixel = channels * bitsPerComponent
        let bytesPerRow = channels * width
        let totalBytes = height * bytesPerRow
        let bitmapInfo = CGBitmapInfo(rawValue: channels == 3 ? CGImageAlphaInfo.none.rawValue : CGImageAlphaInfo.last.rawValue)
        let provider = CGDataProvider( dataInfo: nil,data: data,size: totalBytes,releaseData: {_,_,_  in })!

        return CGImage(width: width,height: height,bitsPerComponent: bitsPerComponent,bitsPerPixel: bitsPerPixel,bytesPerRow: bytesPerRow,space: colorSpaceRef,bitmapInfo: bitmapInfo,provider: provider,decode: nil,shouldInterpolate: false,intent: CGColorRenderingIntent.perceptual)
    }

当我开始频繁地将图像绘制到上下文时,我的应用程序在此功能中崩溃

(context!.draw(cgImage,in: CGRect(x: 0,y: 0,width: frameWidth,高度:frameHeight)))

如果我使用捆绑包中的图像数量并使用此代码创建视频,则其工作正常。当我使用从 [UInt8] 数据创建的 CGImage 时,它​​在写入 3-4 个图像后开始崩溃。

func newPixelBufferFrom(cgImage:CGImage) -> CVPixelBuffer?{
        autoreleasepool {
            
            let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true,kCVPixelBufferCGBitmapContextCompatibilityKey as String: true]
            var pxbuffer:CVPixelBuffer?
            let frameWidth = self.videoSettings[AVVideoWidthKey] as! Int
            let frameHeight = self.videoSettings[AVVideoHeightKey] as! Int
            let status = CVPixelBufferCreate(kCFAllocatorDefault,frameWidth,frameHeight,kCVPixelFormatType_32ARGB,options as CFDictionary?,&pxbuffer)
            assert(status == kCVReturnSuccess && pxbuffer != nil,"newPixelBuffer failed")

            CVPixelBufferLockBaseAddress(pxbuffer!,CVPixelBufferLockFlags(rawValue: 0))
            let pxdata = CVPixelBufferGetBaseAddress(pxbuffer!)
            let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
            let context = CGContext(data: pxdata,height: frameHeight,bitsPerComponent: 8,bytesPerRow: CVPixelBufferGetBytesPerRow(pxbuffer!),space: rgbColorSpace,bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)
            assert(context != nil,"context is nil")
            context!.concatenate(CGAffineTransform.identity)
            context!.draw(cgImage,height: frameHeight))
            CVPixelBufferUnlockBaseAddress(pxbuffer!,CVPixelBufferLockFlags(rawValue: 0))
            return pxbuffer
}

在这里,我使用下面的代码从图像数组创建视频。

typealias CXEMovieMakerCompletion = (URL) -> Void
typealias CXEMovieMakerUIImageExtractor = (AnyObject) -> UIImage?


public class CXEImagesToVideo: NSObject{
    var assetWriter:AVAssetWriter!
    var writeInput:AVAssetWriterInput!
    var bufferAdapter:AVAssetWriterInputPixelBufferAdaptor!
    var videoSettings:[String : Any]!
    var frameTime:CMTime!
    var fileURL:URL!
    
    var completionBlock: CXEMovieMakerCompletion?
    var movieMakerUIImageExtractor:CXEMovieMakerUIImageExtractor?
    
    
    public class func  videoSettings(codec:String,width:Int,height:Int) -> [String: Any]{
        if(Int(width) % 16 != 0){
            print("warning: video settings width must be divisible by 16")
        }
        
        let videoSettings:[String: Any] = [AVVideoCodecKey: AVVideoCodecType.h264,AVVideoWidthKey: width,AVVideoHeightKey: height]
       
        return videoSettings
    }
    
    public init(videoSettings: [String: Any],frameTime: CMTime) {
        super.init()
        self.frameTime = frameTime
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask,true)
        let tempPath = paths[0] + "/exprotvideo1.mp4"
        if(FileManager.default.fileExists(atPath: tempPath)){
            guard (try? FileManager.default.removeItem(atPath: tempPath)) != nil else {
                print("remove path failed")
                return
            }
        }
        
        self.fileURL = URL(fileURLWithPath: tempPath)
        self.assetWriter = try! AVAssetWriter(url: self.fileURL,fileType: AVFileType.mp4)
        
        self.videoSettings = videoSettings
        self.writeInput = AVAssetWriterInput(mediaType: AVMediaType.video,outputSettings: videoSettings)
        assert(self.assetWriter.canAdd(self.writeInput),"add failed")
        
        self.assetWriter.add(self.writeInput)
        let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
        self.bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: self.writeInput,sourcePixelBufferAttributes: bufferAttributes)
        self.frameTime = CMTimeMake(value: 1,timescale: 10)
    }
    
    func createMovieFrom(urls: [URL],withCompletion: @escaping CXEMovieMakerCompletion){
        self.createMovieFromSource(images: urls as [AnyObject],extractor:{(inputObject:AnyObject) ->UIImage? in
                                    return UIImage(data: try! Data(contentsOf: inputObject as! URL))},withCompletion: withCompletion)
    }
    
    func createMovieFrom(images: [UIImage],withCompletion: @escaping CXEMovieMakerCompletion){
        DispatchQueue.main.async {
            self.createMovieFromSource(images: images,extractor: {(inputObject:AnyObject) -> UIImage? in
                                        return inputObject as? UIImage},withCompletion: withCompletion)
        }
        
    }
    func imageFromLayer(layer:CALayer) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(layer.frame.size,layer.isOpaque,0)
        layer.render(in: UIGraphicsGetCurrentContext()!)
        let outputImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return outputImage!
    }
    
    
    
    func createMovieFromSource(images: [AnyObject],extractor: @escaping CXEMovieMakerUIImageExtractor,withCompletion: @escaping CXEMovieMakerCompletion){

        self.completionBlock = withCompletion
        
        self.assetWriter.startWriting()
        self.assetWriter.startSession(atSourceTime: CMTime.zero)
        
        let mediaInputQueue = DispatchQueue.init(label: "Main") // DispatchQueue(label: "mediaInputQueue")
        var i = 0
        let frameNumber = images.count
        
            self.writeInput.requestMediaDataWhenReady(on: mediaInputQueue){
                while(true){
                    if(i >= frameNumber){
                        break
                    }
                    if (self.writeInput.isReadyForMoreMediaData){
                        var sampleBuffer:CVPixelBuffer?
                        autoreleasepool{
                            let temp = images[i]
                            let img = extractor(temp)
                            if img == nil{
                                i += 1
                                print("Warning: counld not extract one of the frames")
                                //continue
                            }

                            sampleBuffer = self.newPixelBufferFrom(cgImage: temp.cgImage!)
                            
                        }
                        if (sampleBuffer != nil){
                            if(i == 0){
                                self.bufferAdapter.append(sampleBuffer!,withPresentationTime: CMTime.zero)
                            }else{
                                let value = i - 1
                                let lastTime = CMTimeMake(value: Int64(value),timescale: self.frameTime.timescale)
                                let presentTime = CMTimeAdd(lastTime,self.frameTime)
                                self.bufferAdapter.append(sampleBuffer!,withPresentationTime: presentTime)
                            }
                            i = i + 1
                        }
                    }
                }
                self.writeInput.markAsFinished()
                self.assetWriter.finishWriting {
                    DispatchQueue.main.sync {
                        self.completionBlock!(self.fileURL)
                    }
                }
            }
    }
    
    func newPixelBufferFrom(cgImage:CGImage) -> CVPixelBuffer?{
        autoreleasepool {
            
            let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true,bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)
            // CGImageAlphaInfo.noneSkipFirst.rawValue
            assert(context != nil,"context is nil")
           // context?.clear(CGRect(x: 0,height: frameHeight))
            context!.concatenate(CGAffineTransform.identity)
            context!.draw(cgImage,CVPixelBufferLockFlags(rawValue: 0))
            return pxbuffer
}
}
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...