kCVPixelFormatType_32BGRA 格式的CMSampleBuffer sampebuffer 如何转化为 AVFrame

import CoreMedia
import libavutil
import libavcodec

func convertSampleBufferToAVFrame(_ sampleBuffer: CMSampleBuffer) -> AVFrame? {
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
        print("Failed to get pixel buffer from sample buffer.")
        return nil
    }
    
    // Lock the pixel buffer
    CVPixelBufferLockBaseAddress(pixelBuffer, [])
    
    let width = CVPixelBufferGetWidth(pixelBuffer)
    let height = CVPixelBufferGetHeight(pixelBuffer)
    let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
    
    // Allocate an AVFrame and set its properties
    let frame = av_frame_alloc()!
    frame.pointee.format = AV_PIX_FMT_BGRA // Pixel format for 32BGRA
    frame.pointee.width = Int32(width)
    frame.pointee.height = Int32(height)
    
    // Allocate buffer for the frame data
    av_frame_get_buffer(frame, 0)
    
    // Copy data from the pixel buffer to the AVFrame
    let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer)!.assumingMemoryBound(to: UInt8.self)
    for i in 0..<height {
        let src = baseAddress + i * bytesPerRow
        let dest = frame.pointee.data[0]! + i * frame.pointee.linesize[0]
        memcpy(dest, src, bytesPerRow)
    }
    
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
    
    return frame
}

AV_PIX_FMT_BGRA 格式 AVFrame 的data 数据 如何存储分布的

FFmpeg中,每种像素格式都由AVPixelFormat枚举表示。对于AV_PIX_FMT_BGRA像素格式:

BGRA 分别代表蓝色、绿色、红色和Alpha(透明度)。
AV_PIX_FMT_BGRA格式指定像素以BGRA的方式紧密存储。这意味着每个像素有4个字节:蓝色1字节,绿色1字节,红色1字节,和Alpha 1字节。
在AVFrame结构中,data成员是指向实际图像数据平面的指针数组。由于AV_PIX_FMT_BGRA是打包的(不是平面的),所有数据都将在data[0]中。

内存中的布局如下:

Copy code
B1, G1, R1, A1, B2, G2, R2, A2, B3, G3, R3, A3, ...
其中B1、G1、R1 和 A1 代表第一个像素的蓝色、绿色、红色和Alpha值;B2、G2、R2 和 A2 代表第二个像素,依此类推。

AVFrame的linesize成员表示每个平面每行的字节数。对于像AV_PIX_FMT_BGRA这样的打包格式,linesize[0]通常是宽度 * 每像素的字节数(对于BGRA,每像素是4字节)。但是,由于内存对齐或填充的考虑,实际的linesize[0]可能大于宽度 * 4。在导航数据中的行时,总是使用AVFrame中的linesize[0]

那么在做 smaplebuffer 转 AVFrame 的时候 为什么使用了

 // Use Swift's UnsafePointer for data copying
        for i in 0..<height {
            let src = baseAddress + i * bytesPerRow
            let dest = frame!.pointee.data.0! + i * Int(frame!.pointee.linesize.0)
            memcpy(dest, src, bytesPerRow)
        }

这个样的拷贝方式 而不是直接将 samplebuffe 的data 拷贝给 frame data.0 呢?

这是因为图像数据在内存中不一定是连续存储的,特别是当图像的宽度不是内存对齐的倍数时。为了提高内存访问性能,图像每行的末尾可能会有一些填充字节,使得每行都对齐到某个字节边界(如4字节或8字节边界)。

bytesPerRow是指示CMSampleBuffer中每行的真实字节数量。它可能等于图像宽度乘以每像素的字节数(对于BGRA是4字节),也可能更大,取决于是否有填充字节。

frame!.pointee.linesize.0是AVFrame中每行的字节数量。它也可能包含填充字节,因此可能与图像的宽度乘以4不同。

由于这两者可能不相等(尤其是在宽度不是对齐的倍数时),我们不能只简单地拷贝整块数据。而是需要逐行拷贝数据,确保每行的数据都被正确地复制到AVFrame中。

简而言之,逐行拷贝是为了确保正确处理任何可能存在的行填充,并确保AVFrame的数据布局与CMSampleBuffer匹配

你可能感兴趣的:(iOS,Swift,iOS,音视频,CMSampleBuffer,AVFrame)