iosswiftxcodevideo-toolboxcore-media

iOS Swift VideoToolBox decompress frame EXC_BAD_ADDRESS error


I was trying to convert CMSampleBuffer to Data over the internet and create a copy of that.

I already compressed the camera capture's CMSampleBuffer by VTCompressionSession with h264.

Now I constructed CMSampleBuffer, created VTDecompressionSession but there's a memory error when at VTDecompressionSessionDecodeFrame() I don't know how to fix.

I saw many h264 examples on the internet but they were all using the legacy Objective-C language.

I am using Xcode 11.3 Swift 5.1 and have deployed to iOS devices that are running iOS 12.1.

    var sps,pps:UnsafePointer<UInt8>?
    var spsSize=0,ppsSize:Int=0
    var parameterSetCount=0
    var nalUnitHeaderLength:Int32=0
    CMVideoFormatDescriptionGetH264ParameterSetAtIndex(CMSampleBufferGetFormatDescription(encodedSamples.last!)!, parameterSetIndex: 0, parameterSetPointerOut: &sps, parameterSetSizeOut: &spsSize, parameterSetCountOut: &parameterSetCount, nalUnitHeaderLengthOut: &nalUnitHeaderLength)
    CMVideoFormatDescriptionGetH264ParameterSetAtIndex(CMSampleBufferGetFormatDescription(encodedSamples.last!)!, parameterSetIndex: 1, parameterSetPointerOut: &pps, parameterSetSizeOut: &ppsSize, parameterSetCountOut: &parameterSetCount, nalUnitHeaderLengthOut: &nalUnitHeaderLength)
    let dataBuffer:CMBlockBuffer={
        var buffer:CMBlockBuffer?
        let bufferData:Data={
            let blockBuffer=CMSampleBufferGetDataBuffer(encodedSamples.last!)!
            var totalLength:Int=0
            var data:UnsafeMutablePointer<Int8>?
            CMBlockBufferGetDataPointer(blockBuffer, atOffset: 0, lengthAtOffsetOut: nil, totalLengthOut: &totalLength, dataPointerOut: &data)
            return Data(bytes: data!,count:totalLength)
        }()
        var bbd=Data()
        bbd.append(bufferData)
        let status=CMBlockBufferCreateWithMemoryBlock(allocator: kCFAllocatorDefault,memoryBlock: &bbd, blockLength: bbd.count,blockAllocator: kCFAllocatorDefault,customBlockSource: nil, offsetToData: 0, dataLength: bbd.count,flags: 0, blockBufferOut: &buffer)
        if status != kCMBlockBufferNoErr{print(status)}
        return buffer!
    }()
    let formatDes:CMFormatDescription={
        let dataParamArray = [sps!,pps!]
        let parameterSetPointers = UnsafePointer<UnsafePointer<UInt8>>(dataParamArray)
        var formatDescription:CMFormatDescription?
        CMVideoFormatDescriptionCreateFromH264ParameterSets(allocator: kCFAllocatorDefault, parameterSetCount: parameterSetCount, parameterSetPointers: parameterSetPointers, parameterSetSizes: UnsafePointer<Int>([spsSize,ppsSize]), nalUnitHeaderLength: nalUnitHeaderLength, formatDescriptionOut: &formatDescription)
        return formatDescription!
    }()
    var timingInfo:CMSampleTimingInfo={
        var t=CMSampleTimingInfo()
        CMSampleBufferGetSampleTimingInfoArray(encodedSamples.last!, entryCount: 1, arrayToFill: &t, entriesNeededOut: nil)
        return t
    }()
    var sampleSize=CMBlockBufferGetDataLength(dataBuffer)
    var sampleBuffer:CMSampleBuffer!
    let status=CMSampleBufferCreate(allocator: kCFAllocatorDefault, dataBuffer: dataBuffer, dataReady: true, makeDataReadyCallback: nil, refcon: nil, formatDescription: formatDes, sampleCount: 1, sampleTimingEntryCount: 1, sampleTimingArray: &timingInfo, sampleSizeEntryCount: 1, sampleSizeArray: &sampleSize, sampleBufferOut: &sampleBuffer)
    var attachmentsArray=CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true) as! Array<Dictionary<String, Any>>
    attachmentsArray[0]=[kCMSampleAttachmentKey_DependsOnOthers as String:false]
    if status != noErr{print(status)}
    if #available(iOS 9, *){
        var session:VTDecompressionSession?
        VTDecompressionSessionCreate(allocator: kCFAllocatorDefault, formatDescription: formatDes, decoderSpecification: nil, imageBufferAttributes: [kCVPixelBufferOpenGLESCompatibilityKey:true,kCVPixelBufferPixelFormatTypeKey:kCVPixelFormatType_32BGRA] as CFDictionary, outputCallback: nil, decompressionSessionOut: &session)
        print(VTDecompressionSessionCanAcceptFormatDescription(session!, formatDescription: formatDes))
        var info=VTDecodeInfoFlags()
        let flags = VTDecodeFrameFlags._EnableAsynchronousDecompression
        let status=VTDecompressionSessionDecodeFrame(session!, sampleBuffer: sampleBuffer, flags: flags, infoFlagsOut: &info){status,infoFlags,imageBuffer,presentationTimeStamp,presentationDuration in
            print(imageBuffer!)
        }//EXC_BAD_ADDRESS HERE!!
        print(status,info)
        VTDecompressionSessionInvalidate(session!)
    }

Solution

  • I find the problem. CMBlockBufferCreateWithMemoryBlock()'s parameter memoryBlock can't use '&Data' as pointer.

    let dataBuffer:CMBlockBuffer={
        var buffer:CMBlockBuffer?
        let bufferData:Data={
            let blockBuffer=CMSampleBufferGetDataBuffer(encodedSamples.last!)!
            var totalLength:Int=0
            var data:UnsafeMutablePointer<Int8>?
            CMBlockBufferGetDataPointer(blockBuffer, atOffset: 0, lengthAtOffsetOut: nil, totalLengthOut: &totalLength, dataPointerOut: &data)
            return Data(bytes: data!,count:totalLength)
        }()
        var bbd=Data()
        bbd.append(bufferData)
        let status=CMBlockBufferCreateWithMemoryBlock(allocator: kCFAllocatorDefault,memoryBlock: UnsafeMutableRawPointer(mutating: (bbd as NSData).bytes), blockLength: bbd.count,blockAllocator: kCFAllocatorDefault,customBlockSource: nil, offsetToData: 0, dataLength: bbd.count,flags: 0, blockBufferOut: &buffer)
        if status != kCMBlockBufferNoErr{print(status)}
        return buffer!
    }()
    

    This is a vary rare problem in Swift language. After I tried to print the data from the CMBlockBuffer I created, I finally solved by using UnsafeMutableRawPointer((data as NSData).bytes).