在Swift中从UImage数组创建视频时出现内存崩溃问题

时间:2019-04-06 03:24:07

标签: ios swift uiimage avvideocomposition

我正在使用Video,我从Uimage数组创建视频。它可以在少量图像时正常工作,但是在大量图像(例如:100多个图像)下,由于内存问题而崩溃。

我已经在创建视频时进行了测试,内存正在增大,但是在完成该过程之后,内存仍然是大小,而不是释放。

任何帮助将不胜感激

这是我的代码:

return [p[0] + p[1]

//创建像素缓冲区

    func buildVideoFromImageArray(completed:@escaping (String)->Void) {

        selectedPhotosArray = arrimageVideo

        imageArrayToVideoURL = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/video1.MP4")
        removeFileAtURLIfExists(url: imageArrayToVideoURL)
        guard let videoWriter = try? AVAssetWriter(outputURL: imageArrayToVideoURL as URL, fileType: AVFileType.mp4) else {
            fatalError("AVAssetWriter error")
        }

        let outputSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : NSNumber(value: Float(outputSize.width)), AVVideoHeightKey : NSNumber(value: Float(outputSize.height))] as [String : Any]
        guard videoWriter.canApply(outputSettings: outputSettings, forMediaType: AVMediaType.video) else {
            fatalError("Negative : Can't apply the Output settings...")
        }
        let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: outputSettings)

        let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(value: Float(outputSize.width)), kCVPixelBufferHeightKey as String: NSNumber(value: Float(outputSize.height))]
        let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
        if videoWriter.canAdd(videoWriterInput) {
            videoWriter.add(videoWriterInput)
        }

        if videoWriter.startWriting() {



            videoWriter.startSession(atSourceTime: kCMTimeZero)
            assert(pixelBufferAdaptor.pixelBufferPool != nil)
            let media_queue = DispatchQueue(label: "mediaInputQueue")


            videoWriterInput.requestMediaDataWhenReady(on: media_queue, using: { () -> Void in
                let fps: Int32 = 1000

                var frameCount: Int64 = 0
                var appendSucceeded = true
                var lastTimeVl :Int64 = 0
                var framePerSecond: Int64 = Int64(0)



                for nextDicData in self.selectedPhotosArray{

                    if (videoWriterInput.isReadyForMoreMediaData) {

                        if let nextImage = nextDicData["img"] as? UIImage
                        {
                            var frameDuration = CMTimeMake(Int64(0), fps)
                            if let timeVl = nextDicData["time"] as? Float{
                                   framePerSecond = Int64(timeVl * 1000)
                                print("TIME FRAME : \(timeVl)")

                            }else{
                                 framePerSecond = Int64(0.1 * 1000)
                            }

                            frameDuration =  CMTimeMake(framePerSecond ,fps)
                            let lastFrameTime = CMTimeMake(Int64(lastTimeVl), fps)
                            let presentationTime =  lastFrameTime


                            if !self.appendPixelBufferForImage(image: nextImage, pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime){
                                      print("AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer")
                                break
                            }





                            if let index = self.selectedPhotosArray.firstIndex(where: { NSDictionary(dictionary: $0).isEqual(to: nextDicData)}){

                                self.selectedPhotosArray.remove(at: index)

                            }


                        }

                    }else{
                        //not ready
                           print("write is Not Ready: \(lastTimeVl)")
                    }
                    if !appendSucceeded {
                        break
                    }
                    frameCount += 1
                   lastTimeVl += framePerSecond

                  usleep(100000)
                }

                videoWriterInput.markAsFinished()

                videoWriter.endSession(atSourceTime: CMTimeMake(lastTimeVl, fps))
                videoWriter.finishWriting { () -> Void in
                    print("-----video1 url = \(self.imageArrayToVideoURL)")
                    self.urlString = "\(self.imageArrayToVideoURL)"
                    self.asset = AVAsset(url: self.imageArrayToVideoURL as URL)


                    DispatchQueue.main.async {
                        //code that caused error goes here
                        completed(self.urlString)
                         self.setupPlayerVideoWithUrl(strUrl: self.urlString)
                    }

                }
            })
        }


    }
func appendPixelBufferForImage(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool {
        var appendSucceeded = false
        autoreleasepool {
            if let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool {
                let pixelBufferPointer = UnsafeMutablePointer<CVPixelBuffer?>.allocate(capacity: 1)
                let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
                    kCFAllocatorDefault,
                    pixelBufferPool,
                    pixelBufferPointer
                )

                if let pixelBuffer = pixelBufferPointer.pointee, status == 0 {
                    fillPixelBufferFromImage(image, pixelBuffer: pixelBuffer)

                    appendSucceeded = pixelBufferAdaptor.append(
                        pixelBuffer,
                        withPresentationTime: presentationTime
                    )

                    pixelBufferPointer.deinitialize(count: 1)
                } else {
                    NSLog("error: Failed to allocate pixel buffer from pool")
                }

                pixelBufferPointer.deallocate()
            }
        }

        return appendSucceeded
    }

1 个答案:

答案 0 :(得分:-1)

我在我的应用程序中遇到了同样的问题,并且在花费了数小时之后。最后我得到了在使用 CVPixelBuffer 绘制图像期间释放内存的解决方案。

请参考下面的代码。

func pixelBufferFromImage(image: UIImage) -> CVPixelBuffer {
        
        let ciimage = CIImage(image: image)
        let tmpcontext = CIContext(options: nil)
        let cgimage =  tmpcontext.createCGImage(ciimage!, from: ciimage!.extent)
        
        let cfnumPointer = UnsafeMutablePointer<UnsafeRawPointer>.allocate(capacity: 1)
        let cfnum = CFNumberCreate(kCFAllocatorDefault, .intType, cfnumPointer)
        let keys: [CFString] = [kCVPixelBufferCGImageCompatibilityKey, kCVPixelBufferCGBitmapContextCompatibilityKey, kCVPixelBufferBytesPerRowAlignmentKey]
        let values: [CFTypeRef] = [kCFBooleanTrue, kCFBooleanTrue, cfnum!]
        let keysPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
        let valuesPointer =  UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
        keysPointer.initialize(to: keys)
        valuesPointer.initialize(to: values)
        let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true, kCVPixelBufferCGBitmapContextCompatibilityKey as String: true]
       
        let width = Int(image.size.width)
        let height = Int(image.size.width)
     
        var pxbuffer: CVPixelBuffer?
        // if pxbuffer = nil, you will get status = -6661
        var status = CVPixelBufferCreate(kCFAllocatorDefault, width, height,
                                         kCVPixelFormatType_32BGRA, options as CFDictionary, &pxbuffer)
        assert(status == kCVReturnSuccess && pxbuffer != nil, "newPixelBuffer failed")

        status = CVPixelBufferLockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0));
        
        let bufferAddress = CVPixelBufferGetBaseAddress(pxbuffer!);

        let rgbColorSpace = CGColorSpaceCreateDeviceRGB();
        let bytesperrow = CVPixelBufferGetBytesPerRow(pxbuffer!)
        let context = CGContext(data: bufferAddress,
                                width: width,
                                height: height,
                                bitsPerComponent: 8,
                                bytesPerRow: bytesperrow,
                                space: rgbColorSpace,
                                bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue);
        context?.concatenate(CGAffineTransform(rotationAngle: 0))            
        context?.draw(cgimage!, in: CGRect(x:0, y:0, width:CGFloat(width), height:CGFloat(height)));
        status = CVPixelBufferUnlockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0));
        return pxbuffer!;
        
    }