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
}
在FFmpeg中,每种像素格式都由AVPixelFormat枚举表示。对于AV_PIX_FMT_BGRA像素格式:
B、G、R 和 A 分别代表蓝色、绿色、红色和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)
}
这是因为图像数据在内存中不一定是连续存储的,特别是当图像的宽度不是内存对齐的倍数时。为了提高内存访问性能,图像每行的末尾可能会有一些填充字节,使得每行都对齐到某个字节边界(如4字节或8字节边界)。
bytesPerRow是指示CMSampleBuffer中每行的真实字节数量。它可能等于图像宽度乘以每像素的字节数(对于BGRA是4字节),也可能更大,取决于是否有填充字节。
frame!.pointee.linesize.0是AVFrame中每行的字节数量。它也可能包含填充字节,因此可能与图像的宽度乘以4不同。
由于这两者可能不相等(尤其是在宽度不是对齐的倍数时),我们不能只简单地拷贝整块数据。而是需要逐行拷贝数据,确保每行的数据都被正确地复制到AVFrame中。
简而言之,逐行拷贝是为了确保正确处理任何可能存在的行填充,并确保AVFrame的数据布局与CMSampleBuffer匹配