Скопируйте CVPixelBuffer на любое устройство iOS - PullRequest
0 голосов
/ 03 ноября 2018

У меня возникли большие трудности с созданием кода, который надежно копирует CVPixelBuffer на любое устройство iOS. Моя первая попытка работала нормально, пока я не попробовал это на iPad Pro:

extension CVPixelBuffer {
    func deepcopy() -> CVPixelBuffer? {
        let width = CVPixelBufferGetWidth(self)
        let height = CVPixelBufferGetHeight(self)
        let format = CVPixelBufferGetPixelFormatType(self)
        var pixelBufferCopyOptional:CVPixelBuffer?
        CVPixelBufferCreate(nil, width, height, format, nil, &pixelBufferCopyOptional)
        if let pixelBufferCopy = pixelBufferCopyOptional {
            CVPixelBufferLockBaseAddress(self, kCVPixelBufferLock_ReadOnly)
            CVPixelBufferLockBaseAddress(pixelBufferCopy, 0)
            let baseAddress = CVPixelBufferGetBaseAddress(self)
            let dataSize = CVPixelBufferGetDataSize(self)
            let target = CVPixelBufferGetBaseAddress(pixelBufferCopy)
            memcpy(target, baseAddress, dataSize)
            CVPixelBufferUnlockBaseAddress(pixelBufferCopy, 0)
            CVPixelBufferUnlockBaseAddress(self, kCVPixelBufferLock_ReadOnly)
        }
        return pixelBufferCopyOptional
    }
}

Вышеуказанный сбой на iPad Pro, потому что CVPixelBufferGetDataSize(self) немного больше, чем CVPixelBufferGetDataSize(pixelBufferCopy), поэтому memcpy пишет в нераспределенную память.

Так что я сдался и попробовал это:

func copy() -> CVPixelBuffer?
{
    precondition(CFGetTypeID(self) == CVPixelBufferGetTypeID(), "copy() cannot be called on a non-CVPixelBuffer")

    var _copy: CVPixelBuffer?

    CVPixelBufferCreate(
        nil,
        CVPixelBufferGetWidth(self),
        CVPixelBufferGetHeight(self),
        CVPixelBufferGetPixelFormatType(self),
        CVBufferGetAttachments(self, .shouldPropagate),
        &_copy)

    guard let copy = _copy else { return nil }

    CVPixelBufferLockBaseAddress(self, .readOnly)
    CVPixelBufferLockBaseAddress(copy, [])
    defer
    {
        CVPixelBufferUnlockBaseAddress(copy, [])
        CVPixelBufferUnlockBaseAddress(self, .readOnly)
    }

    for plane in 0 ..< CVPixelBufferGetPlaneCount(self)
    {
        let dest        = CVPixelBufferGetBaseAddressOfPlane(copy, plane)
        let source      = CVPixelBufferGetBaseAddressOfPlane(self, plane)
        let height      = CVPixelBufferGetHeightOfPlane(self, plane)
        let bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(self, plane)

        memcpy(dest, source, height * bytesPerRow)
    }

    return copy
}

Это работает на обоих моих тестовых устройствах, но оно только дошло до реальных клиентов, и оказывается, что он выходит из строя на iPad 6 (и пока только на этом устройстве). Это EXC_BAD_ACCESS при вызове на memcpy() снова.

Кажется сумасшедшим, что для этого нет простого API-вызова, учитывая, как трудно сделать его надежным. Или я делаю это сложнее, чем нужно? Спасибо за любой совет!

1 Ответ

0 голосов
/ 13 ноября 2018

Вторая реализация выглядит довольно солидно. Единственная проблема, которую я могу себе представить, заключается в том, что плоскость в новом пиксельном буфере имеет другую длину шага (байт на строку). Длина шага основана на ширине × (байт на пиксель) и затем округляется неопределенным образом для достижения оптимального доступа к памяти.

Так проверьте, если:

CVPixelBufferGetBytesPerRowOfPlane(self, plane) == CVPixelBufferGetBytesPerRowOfPlane(copy, plane

Если нет, скопируйте строку пикселя строка за строкой:

for plane in 0 ..< CVPixelBufferGetPlaneCount(self)
{
    let dest            = CVPixelBufferGetBaseAddressOfPlane(copy, plane)
    let source          = CVPixelBufferGetBaseAddressOfPlane(self, plane)
    let height          = CVPixelBufferGetHeightOfPlane(self, plane)
    let bytesPerRowSrc  = CVPixelBufferGetBytesPerRowOfPlane(self, plane)
    let bytesPerRowDest = CVPixelBufferGetBytesPerRowOfPlane(copy, plane)

    if bytesPerRowSrc == bytesPerRowDest {
        memcpy(dest, source, height * bytesPerRowSrc)
    } else {
        var startOfRowSrc = source
        var startOfRowDest = dest
        for _ in 0..<height {
            memcpy(startOfRowDest, startOfRowSrc, min(bytesPerRowSrc, bytesPerRowDest))
            startOfRowSrc += bytesPerRowSrc
            startOfRowDest += bytesPerRowDest
        }
    }
}
...