如何在Swift中从UnsafeMutablePointer<UInt8>获取CVPixelBuffer句柄?

3

我得到了一个解码后的AVFrame,其格式显示为160/Videotoolbox_vld。在查阅了一些文章(这里)并查看了FFmpeg源代码(这里这里)之后,CVBuffer句柄应该在AVFrame.data[3]处。但是我得到的CVBuffer似乎无效,任何CVPixelBufferGetXXX()函数都返回0或nil。

如果我像ffmpeg的example/hw_decode.c那样使用av_hwframe_transfer_data(),则可以将示例从HW下载到SW缓冲区。它的AVFrame.format将是nv12。通过sws_scale转换为bgra后,示例可以在视图上显示正确的内容。
我认为VideoToolbox解码帧没问题。我将AVFrame.data[3]转换为CVBuffer的方式可能有误。我刚学习如何在Swift中访问C指针,但我不确定如何正确读取指针中的资源句柄(CVBuffer)。
以下是我尝试从AVFrame中提取CVBuffer的方式。
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, ...) 


}
4个回答

1

CVBuffer不是固定大小的,因此重新绑定内存不会以这种方式起作用。你需要这样做:

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

然而,问题在于FFmpeg的VideoToolbox后端没有使用kCVPixelBufferMetalCompatibilityKey设置为true创建CVPixelBuffer。无论如何,您都无法成功调用CVMetalTextureCacheCreateTextureFromImage(...)
您可以考虑使用具有适当设置(包括将kCVPixelBufferMetalCompatibilityKey设置为true)的CVPixelBufferPool,然后使用VTPixelTransferSession快速将FFmpeg的像素缓冲区复制到您自己的缓冲区中。

0
/**
    @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?

也许您可以尝试在使用CVPixelBufferGetBaseAddressOfPlane之前使用CVPixelBufferLockBaseAddress。

0

看起来我将 void* 错误地转换成了 CVPixelBuffer*,而不是直接将 void* 转换成 CVPixelBuffer。我找不到一种快捷的方法将指针类型转换成数字类型。(使用 as! CVPixelBuffer 会导致崩溃)。

因此我在C代码中创建了一个函数,用于将 void* 转换成 CVPixelBufferRef 执行这样的转换工作。

// 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

1
刚刚检查了一下,我的一行代码回答是正确的,也是最直接的方法,无需将 obj-c 引入其中。 - Greg Cotten

0

试试这个

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)

我曾尝试过这个,但失败了。所有的CVPixelXX Getter函数都返回无效数据。我认为它只是转换成了CVPixelBuffer*而不是直接的CVPixelBuffer。 - Chen OT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接