Как получить дескриптор CVPixelBuffer из UnsafeMutablePointer‹UInt8› в Swift?

Я получил декодированный AVFrame, формат которого показывает 160/Videotoolbox_vld. После того, как погуглил несколько статей (здесь) и просмотрела исходный код FFmpeg (здесь и здесь), дескриптор CVBuffer должен быть в AVFrame.data[3]. Но CVBuffer, которое я получил, кажется недействительным, любая функция CVPixelBufferGetXXX() возвращает 0 или ноль.

Если бы я использовал av_hwframe_transfer_data(), как example/hw_decode. c семпл можно загрузить из буфера HW в SW. Его AVFrame.format будет nv12. После преобразования с помощью sws_scale в bgra образец может быть показан для просмотра с правильным содержанием.

Я думаю, что декодированный кадр VideoToolbox в порядке. То, как я конвертирую AVFrame.data[3] в CVBuffer, может быть неправильным. Только что научился обращаться к указателю c в Swift, но я не уверен, как правильно читать дескриптор ресурса (CVBuffer) в указателе.

Ниже показано, как я пытаюсь извлечь CVBuffer из AVFrame.

var pFrameOpt: UnsafeMutablePointer<AVFrame>? = av_frame_alloc()
avcodec_receive_frame(..., pFrameOpt)

let data3: UnsafeMutablePointer<UInt8>? = pFrameOpt?.pointee.data.3
data3?.withMemoryRebound(to: CVBuffer.self, capacity: 1) { pCvBuf in
    let fW = pFrameOpt!.pointee.width // print 3840
    let fH = pFrameOpt!.pointee.height // print 2160
    let fFmt = pFrameOpt!.pointee.format // print 160

    let cvBuf: CVBuffer = pCvBuf.pointee

    let a1 = CVPixelBufferGetDataSize(cvBuf)               // print 0
    let a2 = CVPixelBufferGetPixelFormatType(cvBuf)        // print 0
    let a3 = CVPixelBufferGetWidth(cvBuf)                  // print 0
    let a4 = CVPixelBufferGetHeight(cvBuf)                 // print 0
    let a5 = CVPixelBufferGetBytesPerRow(cvBuf)            // print 0
    let a6 = CVPixelBufferGetBytesPerRowOfPlane(cvBuf, 0)  // print 0
    let a7 = CVPixelBufferGetWidthOfPlane(cvBuf, 0)        // print 0
    let a8 = CVPixelBufferGetHeightOfPlane(cvBuf, 0)       // print 0
    let a9 = CVPixelBufferGetPlaneCount(cvBuf)             // print 0
    let a10 = CVPixelBufferIsPlanar(cvBuf)                 // print false
    let a11 = CVPixelBufferGetIOSurface(cvBuf)             // print nil
    let a12 = CVPixelBufferGetBaseAddress(cvBuf)           // print nil
    let a13 = CVPixelBufferGetBaseAddressOfPlane(cvBuf, 0) // print nil

    let b1 = CVImageBufferGetCleanRect(cvBuf)   // print 0, 0, 0, 0
    let b2 = CVImageBufferGetColorSpace(cvBuf)  // print nil 
    let b3 = CVImageBufferGetDisplaySize(cvBuf) // print 0, 0, 0, 0
    let b4 = CVImageBufferGetEncodedSize(cvBuf) // print 0, 0, 0, 0
    let b5 = CVImageBufferIsFlipped(cvBuf)      // print false

    // bad exec
    var cvTextureOut: CVMetalTexture?
    CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, ..., cvBuf, nil, .bgra8Unorm, 3840, 2160, 0, ...) 


}

person Chen OT    schedule 12.08.2019    source источник


Ответы (4)


CVBuffer не является фиксированным размером, поэтому перепривязка памяти не будет работать таким образом. Вам нужно сделать это:

Unmanaged<CVBuffer>.fromOpaque(data!).takeRetainedValue()

Однако суть в том, что серверная часть FFmpeg VideoToolbox не создает CVPixelBuffer с kCVPixelBufferMetalCompatibilityKey, установленным на true. В любом случае вы не сможете успешно вызвать CVMetalTextureCacheCreateTextureFromImage(...).

Вы можете использовать CVPixelBufferPool с соответствующими настройками (включая kCVPixelBufferMetalCompatibilityKey, установленное на true), а затем использовать VTPixelTransferSession для быстрого копирования пиксельного буфера FFmpeg в свой собственный.

person Greg Cotten    schedule 15.11.2019

Кажется, я неправильно привел void* к CVPixelBuffer* вместо того, чтобы привести void* непосредственно к CVPixelBuffer. Я не могу найти быстрый способ сделать такое приведение стиля c из указателя в числовое значение. (Использование as! CVPixelBuffer вызывает сбой).

Поэтому я создаю функцию от void* до CVPixelBufferRef в коде C, чтобы выполнять такую ​​работу приведения.

// util.h
#include <CoreVideo/CVPixelBuffer.h>
CVPixelBufferRef CastToCVPixelBuffer(void* p);
// util.c
CVPixelBufferRef CastToCVPixelBuffer(void* p)
{
    return (CVPixelBufferRef)p;
}
// BridgeHeader.h
#include "util.h"

Затем передайте UnsafeMutablePointer<UInt8> и получите дескриптор CVPixelBuffer.

let pFrameOpt: UnsafeMutablePointer<AVFrame>? = ...
let data3: UnsafeMutablePointer<UInt8>? = pFrameOpt?.pointee.data.3

let cvBuf: CVBuffer = CastToCVPixelBuffer(data3).takeUnretainedValue()

let width = CVPixelBufferGetWidth(cvBuf)   // print 3840
let height = CVPixelBufferGetHeight(cvBuf) // print 2160
person Chen OT    schedule 13.08.2019
comment
Только что проверил. Мой однострочный ответ правильный и самый простой способ сделать это без необходимости привлекать obj-c. - person Greg Cotten; 02.04.2020

Попробуй это

let cvBuf: CVBuffer = Array(UnsafeMutableBufferPointer(start: data3, count: 3))
.withUnsafeBufferPointer {
   $0.baseAddress!.withMemoryRebound(to: CVBuffer.self, capacity: 1) { $0 }
}.pointee

или, может быть, даже

let cvBuf: CVBuffer = unsafeBitcast(UnsafeMutableBufferPointer(start: data3, count: 3), to: CVBuffer.self)
person Kamil.S    schedule 23.08.2019
comment
Я пробовал это и потерпел неудачу. Все функции получения CVPixelXX возвращают неверные данные. Я думаю, что он просто конвертировался в CVPixelBuffer* вместо CVPixelBuffer напрямую. - person Chen OT; 24.08.2019

/**
    @function   CVPixelBufferGetBaseAddressOfPlane
    @abstract   Returns the base address of the plane at planeIndex in the PixelBuffer.
    @discussion Retrieving the base address for a PixelBuffer requires that the buffer base address be locked
                via a successful call to CVPixelBufferLockBaseAddress. On OSX 10.10 and earlier, or iOS 8 and
                earlier, calling this function with a non-planar buffer will have undefined behavior.
    @param      pixelBuffer Target PixelBuffer.
    @param      planeIndex  Identifying the plane.
    @result     Base address of the plane, or NULL for non-planar CVPixelBufferRefs.
*/
@available(iOS 4.0, *)
public func CVPixelBufferGetBaseAddressOfPlane(_ pixelBuffer: CVPixelBuffer, _ planeIndex: Int) -> UnsafeMutableRawPointer?

возможно, вы можете попробовать использовать CVPixelBufferLockBaseAddress перед использованием CVPixelBufferGetBaseAddressOfPlane

person cl zhu    schedule 08.03.2021