Код: Выделить всё
CVImageBuffer
Есть ли какие-нибудь решения по его улучшению?
Или единственный способ повысить производительность — использовать OpenGL/Metal? Но не уверен, насколько лучше, если нам все равно придется каким-то образом передавать буфер пикселей в AVAssetWriter. Есть ли какой-нибудь простой пример использования Metal и AVAssetWriter, аналогичный следующему примеру кода?
Код: Выделить всё
private let cIContext: CIContext = {
if let mtlDevice = MTLCreateSystemDefaultDevice() {
return CIContext(mtlDevice: mtlDevice) // makes no difference in perfomance for CIContext.render()
} else {
return CIContext()
}
}()
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let assetWriter = assetWriter, let videoWriterInput = videoWriterInput else { return }
if isRecording == false || assetWriter.status != .writing { return }
if CMSampleBufferDataIsReady(sampleBuffer) == false {
return
}
if output == videoOutput, videoWriterInput.isReadyForMoreMediaData {
let presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
if hasWritingSessionStarted == false {
assetWriter.startSession(atSourceTime: presentationTime)
hasWritingSessionStarted = true
guard let pixelBufferPool = pixelBufferAdaptor?.pixelBufferPool else { return }
let status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBuffer)
guard status == kCVReturnSuccess else {
print("Failed to create pixel buffer")
return
}
}
guard let pixelBuffer = pixelBuffer else {
print("Pixel buffer is nil")
return
}
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer.")
return
}
// fast, up to ~1 ms
let ciImage = CIImage(cvPixelBuffer: imageBuffer)
// fast, up to ~1 ms
let compositedImage = watemarkImage.composited(over: ciImage)
var tookTime = CFAbsoluteTimeGetCurrent()
// very slow, ~30 ms for 4K resolution on iPhone 11 (base)
cIContext.render(compositedImage, to: pixelBuffer)
tookTime = CFAbsoluteTimeGetCurrent() - tookTime
// fast, up to ~1 ms
pixelBufferAdaptor?.append(pixelBuffer, withPresentationTime: presentationTime)
print("cIContext.render took \(tookTime * 1000) ms")
}
}
Мне удалось преобразовать CIImage в MTLTexture, и затем MTLTexture в CVPixelBuffer, работает немного быстрее, но по какой-то причине изображение перевернуто по вертикали:
Код: Выделить всё
// before starting the recording:
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .bgra8Unorm, // Choose a suitable pixel format
width: Int(frameSize.width),
height: Int(frameSize.height),
mipmapped: false
)
textureDescriptor.usage = [.shaderWrite] // Important!
// Create the Metal texture
texture = mtlDevice.makeTexture(descriptor: textureDescriptor)
// in captureOutput function:
var ciImage = CIImage(cvPixelBuffer: imageBuffer)
// additionally process CIImage (add some filters) and get a new CIImage
let commandBuffer = commandQueue.makeCommandBuffer()
ciContext.render(
ciImage,
to: texture,
commandBuffer: commandBuffer,
bounds: CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height),
colorSpace: CGColorSpaceCreateDeviceRGB()
)
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
let width = texture.width
let height = texture.height
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
if let pixelBufferBaseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) {
texture.getBytes(
pixelBufferBaseAddress,
bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
from: MTLRegionMake2D(0, 0, width, height),
mipmapLevel: 0
)
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
pixelBufferAdaptor?.append(pixelBuffer, withPresentationTime: presentationTime)
Обновление 2:
Это кажется, Металл работает именно так. В любом случае я только что использовал videoWriterInput.transform = CGAffineTransform(scaleX: 1, y: -1), чтобы «исправить это». Я пытался выполнить такое преобразование для CIImage перед преобразованием его в MLTexture, но по какой-то причине это не сработало.
Этот метод занимает около 23 мс вместо ~30 мс, так что это все еще не так. достаточно.
p.s.:
Я также собираюсь попытаться получить MLTexture непосредственно из CMSampleBuffer и добавить к нему фильтры (я думаю это будет сложнее по сравнению с фильтрами CIImage). Я попробовал следующий неожиданный результат преобразования CVPixelBuffer в код MTLTexture, но также столкнулся с проблемой, упомянутой автором
Обновление 3:
О прямой MLTexture из буфера камеры:
Потому что в принципе это может быть формат YUV (
Код: Выделить всё
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
var lumaTexture: CVMetalTexture?
var chromaTexture: CVMetalTexture?
и объединить их в два, преобразовать YUV в RGB с помощью Metal Shader.
Мне тоже удалось это сделать, и производительность очень хорошая, около 6-7 мс на все вещи (включая кодирование видео) в функции captureOutput.
Теперь появилась новая задача: как-то добавить водяной знак/наложение текста в MLTexture, потому что теперь у меня нет CIImage больше
Подробнее здесь: https://stackoverflow.com/questions/790 ... cpu-time-u