swift 使用AVCaptureSession到AVAssetWriter(使用HLS的AVAssetWriterDelegate)时发生内存泄漏

zbsbpyhn  于 2023-02-28  发布在  Swift
关注(0)|答案(1)|浏览(169)

我们发现AVAssetWriter在使用所需的代理AVAssetWriterDelegate创建HLS fMP4视频时会泄漏内存。甚至在使用给定的片段数据进行处理和存储之前。
当手动释放内存时(感觉这是错误的),内存泄漏似乎消失了。
即使在最小的情况下,记忆也会迅速增长。

import Cocoa
import AVFoundation

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        AVCaptureDevice.requestAccess(for: .video, completionHandler: {
            _ in
        })
    }

    private var fileWriter: AVAssetWriter!
    private var videoInput: AVAssetWriterInput!
    private var bufferAdaptor: AVAssetWriterInputPixelBufferAdaptor!
    private var captureSession: AVCaptureSession!
    internal let recordingQueue = DispatchQueue(label: "RecordingQueue", qos: .userInitiated)
    internal let writerQueue = DispatchQueue(label: "WriterQueue", qos: .userInitiated)

    @IBAction func startCapture(_ sender: NSButton) {
        
        self.writerQueue.async {
            let device = AVCaptureDevice.default(for: .video)!
            
            try! device.lockForConfiguration()
            
            device.activeFormat = device.formats.last!
            device.activeVideoMaxFrameDuration = CMTime(value: 1, timescale: 25)
            device.activeVideoMinFrameDuration = CMTime(value: 1, timescale: 25)
            device.unlockForConfiguration()

            self.fileWriter = AVAssetWriter(contentType: .mpeg4Movie)
            self.fileWriter.preferredOutputSegmentInterval = CMTime(seconds: 0.2, preferredTimescale: 60000)
            self.fileWriter.outputFileTypeProfile = .mpeg4AppleHLS
            self.fileWriter.initialSegmentStartTime = .zero
            
            let videoOutputSettings: [String: Any] = [
                AVVideoWidthKey: 1920,
                AVVideoHeightKey: 1080,
                AVVideoCodecKey: AVVideoCodecType.h264,
                AVVideoCompressionPropertiesKey: [
                    AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
                    AVVideoAverageBitRateKey: 6000 * 1024
                ]
            ]

            self.videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoOutputSettings)
            self.fileWriter.movieTimeScale = CMTimeScale(exactly: 25)!
            self.videoInput.mediaTimeScale = CMTimeScale(exactly: 25)!
            
            self.videoInput.expectsMediaDataInRealTime = true
            self.videoInput.performsMultiPassEncodingIfSupported = false
            
            let sourcePixelBufferAttributes:[String:Any] = [
                kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32ARGB,
                kCVPixelBufferMetalCompatibilityKey as String: true,
            ]
            self.bufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: self.videoInput, sourcePixelBufferAttributes: sourcePixelBufferAttributes)
            
            if self.fileWriter.canAdd(self.videoInput) {
                self.fileWriter.add(self.videoInput)
            } else {
                return
            }
            
            self.fileWriter.delegate = self

            self.captureSession = AVCaptureSession()
            self.captureSession?.beginConfiguration()

            let videoInput = try! AVCaptureDeviceInput(device: device)
            if self.captureSession?.canAddInput(videoInput) ?? false {
                self.captureSession?.addInput(videoInput)
            } else {
                return
            }
        
            self.captureSession?.sessionPreset = AVCaptureSession.Preset.high

            let videoDataOutput = AVCaptureVideoDataOutput()
            videoDataOutput.setSampleBufferDelegate(self, queue: self.recordingQueue)
            videoDataOutput.alwaysDiscardsLateVideoFrames = true
            videoDataOutput.videoSettings = [
                kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
                kCVPixelBufferMetalCompatibilityKey as String: true
            ]
            
            if self.captureSession?.canAddOutput(videoDataOutput) ?? false {
                self.captureSession?.addOutput(videoDataOutput)
            } else {
                return
            }
            self.captureSession?.commitConfiguration()

            try! device.lockForConfiguration()
            self.captureSession?.startRunning()
            device.unlockForConfiguration()

            self.fileWriter.startWriting()
            self.fileWriter.startSession(atSourceTime: CMTime.zero)
        }
    }
    
    func write(sample: CMSampleBuffer) {
        if self.videoInput.isReadyForMoreMediaData {
            self.videoInput.append(sample)
        }
    }
}

extension ViewController: AVAssetWriterDelegate {
    func assetWriter(_ writer: AVAssetWriter, didOutputSegmentData segmentData: Data, segmentType: AVAssetSegmentType, segmentReport: AVAssetSegmentReport?) {
        print(segmentData.count)
        
//      let _ = segmentData.withUnsafeBytes {
//          raw in
//          raw.baseAddress?.deallocate()
//      }
    }
}

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        self.write(sample: sampleBuffer)
    }
}

在一个新项目中运行这段小代码,你会看到内存在增长。取消对委托中的行的注解,结果如预期。
我们遗漏了什么?或者我们发现了一个bug?(已经发送到苹果了)。
任何能堵住漏洞的想法都欢迎...

enxuqcxy

enxuqcxy1#

苹果已经解决了macOS 13.3测试版中的内存泄漏问题。经过反复检查,它似乎确实得到了修复。示例代码中没有出现更多的泄漏。

相关问题