我正在尝试扩展CVPixelBuffer,以便通过使用填充重新初始化CVPixelBuffer来访问缓冲区外部的内存不会导致EXC_BAD_ACCESS错误。但是,它似乎不起作用。任何关于我做错事的提示将不胜感激。

    let paddingLeft = abs(min(cropX, 0))
    let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
    let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
    let paddingTop = abs(min(cropY, 0))

    let attr = [kCVPixelBufferExtendedPixelsLeftKey: paddingLeft*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsTopKey: paddingTop*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsRightKey: paddingRight*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsBottomKey: paddingBottom*40 + 1 as CFNumber]

    guard kCVReturnSuccess == CVPixelBufferCreateWithBytes(kCFAllocatorDefault, srcWidth, srcHeight, pixelFormat, srcData, srcBytesPerRow, nil, nil, attr as CFDictionary, &paddedSrcPixelBuffer) else {
            print("failed to allocate a new padded pixel buffer")
            return nil
        }


对于扩展的CVPixelBuffer,应根据我的理解定义访问CVPixelBuffer外部的数据(例如,当x,y为负数或大于缓冲区的宽度/高度时)。但是,以下代码在带有EXC_BAD_ACCESS代码1的VImageScale_ARGB8888内部的最后一行崩溃。

据推测这意味着正在访问的数据未映射。

guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
            print("Error: could not get padded pixel buffer base address")
            return nil
        }

srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: offset),
                              height: vImagePixelCount(cropHeight),
                              width: vImagePixelCount(cropWidth),
                              rowBytes: srcBytesPerRow)
let destBytesPerRow = scaleWidth*4
let destData = malloc(scaleHeight*destBytesPerRow)

var destBuffer = vImage_Buffer(data: destData,
                                   height: vImagePixelCount(scaleHeight),
                                   width: vImagePixelCount(scaleWidth),
                                   rowBytes: destBytesPerRow)

let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags) // crashes here due to EXC_BAD_ACCESS Code: 1


非常感谢!

最佳答案

这是调整大小功能的修改版本,它将通过将内存的连续部分复制到具有正确形状的新分配的缓冲区中来创建填充缓冲区。

public func resizePixelBuffer(_ srcPixelBuffer: CVPixelBuffer,
                              cropX: Int,
                              cropY: Int,
                              cropWidth: Int,
                              cropHeight: Int,
                              scaleWidth: Int,
                              scaleHeight: Int) -> CVPixelBuffer? {
    let flags = CVPixelBufferLockFlags(rawValue: 0)
    let pixelFormat = CVPixelBufferGetPixelFormatType(srcPixelBuffer)
    guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(srcPixelBuffer, flags) else {
        return nil
    }
    defer { CVPixelBufferUnlockBaseAddress(srcPixelBuffer, flags) }

    guard let srcData = CVPixelBufferGetBaseAddress(srcPixelBuffer) else {
        print("Error: could not get pixel buffer base address")
        return nil
    }

    let srcHeight = CVPixelBufferGetHeight(srcPixelBuffer)
    let srcWidth = CVPixelBufferGetWidth(srcPixelBuffer)
    let srcBytesPerRow = CVPixelBufferGetBytesPerRow(srcPixelBuffer)
    let offset = cropY*srcBytesPerRow + cropX*4

    var srcBuffer: vImage_Buffer!
    var paddedSrcPixelBuffer: CVPixelBuffer!

    if (cropX < 0 || cropY < 0 || cropX + cropWidth > srcWidth || cropY + cropHeight > srcHeight) {
        let paddingLeft = abs(min(cropX, 0))
        let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
        let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
        let paddingTop = abs(min(cropY, 0))

        let paddedHeight = paddingTop + srcHeight + paddingBottom
        let paddedWidth = paddingLeft + srcWidth + paddingRight

        guard kCVReturnSuccess == CVPixelBufferCreate(kCFAllocatorDefault, paddedWidth, paddedHeight, pixelFormat, nil, &paddedSrcPixelBuffer) else {
            print("failed to allocate a new padded pixel buffer")
            return nil
        }

        guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(paddedSrcPixelBuffer, flags) else {
            return nil
        }

        guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
            print("Error: could not get padded pixel buffer base address")
            return nil
        }

        let paddedBytesPerRow = CVPixelBufferGetBytesPerRow(paddedSrcPixelBuffer)
        for yIndex in paddingTop..<srcHeight+paddingTop {
            let dstRowStart = paddedSrcData.advanced(by: yIndex*paddedBytesPerRow).advanced(by: paddingLeft*4)
            let srcRowStart = srcData.advanced(by: (yIndex - paddingTop)*srcBytesPerRow)
            dstRowStart.copyMemory(from: srcRowStart, byteCount: srcBytesPerRow)
        }

        let paddedOffset = (cropY + paddingTop)*paddedBytesPerRow + (cropX + paddingLeft)*4
        srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: paddedOffset),
                                  height: vImagePixelCount(cropHeight),
                                  width: vImagePixelCount(cropWidth),
                                  rowBytes: paddedBytesPerRow)

    } else {
        srcBuffer = vImage_Buffer(data: srcData.advanced(by: offset),
                                  height: vImagePixelCount(cropHeight),
                                  width: vImagePixelCount(cropWidth),
                                  rowBytes: srcBytesPerRow)
    }

    let destBytesPerRow = scaleWidth*4
    guard let destData = malloc(scaleHeight*destBytesPerRow) else {
        print("Error: out of memory")
        return nil
    }
    var destBuffer = vImage_Buffer(data: destData,
                                   height: vImagePixelCount(scaleHeight),
                                   width: vImagePixelCount(scaleWidth),
                                   rowBytes: destBytesPerRow)

    let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
    let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags)
    if error != kvImageNoError {
        print("Error:", error)
        free(destData)
        return nil
    }

    let releaseCallback: CVPixelBufferReleaseBytesCallback = { _, ptr in
        if let ptr = ptr {
            free(UnsafeMutableRawPointer(mutating: ptr))
        }
    }

    var dstPixelBuffer: CVPixelBuffer?
    let status = CVPixelBufferCreateWithBytes(nil, scaleWidth, scaleHeight,
                                              pixelFormat, destData,
                                              destBytesPerRow, releaseCallback,
                                              nil, nil, &dstPixelBuffer)
    if status != kCVReturnSuccess {
        print("Error: could not create new pixel buffer")
        free(destData)
        return nil
    }

    if paddedSrcPixelBuffer != nil {
        CVPixelBufferUnlockBaseAddress(paddedSrcPixelBuffer, flags)
    }


    return dstPixelBuffer
}

关于swift - 访问已通过填充扩展的CVPixelBuffer外部的像素,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53734335/

10-11 17:15