Ошибка CVPixelBufferPool ( kCVReturnInvalidArgument/-6661)
Я реализовал предыдущие предложения с Swift ( Как использовать CVPixelBufferPool в сочетании с AVAssetWriterInputPixelBufferAdaptor в iPhone?), Но застрял с "kCVReturnInvalidArgument" (значение ошибки: -6661) при использовании CVPixelBufferPoolCreatedPoolBuuisedPuolised.
Я в основном пытаюсь создать фильм из изображений, но так как пул буферов не создается успешно, я не могу добавить пиксельные буферы - вот мой код для этого.
Любые предложения высоко ценятся!
import Foundation
import Photos
import OpenGLES
import AVFoundation
import CoreMedia
class MovieGenerator {
var _videoWriter:AVAssetWriter
var _videoWriterInput: AVAssetWriterInput
var _adapter: AVAssetWriterInputPixelBufferAdaptor
var _buffer = UnsafeMutablePointer<Unmanaged<CVPixelBuffer>?>.alloc(1)
init(frameSize size: CGSize, outputURL url: NSURL) {
// delete file if exists
let sharedManager = NSFileManager.defaultManager() as NSFileManager
if(sharedManager.fileExistsAtPath(url.path!)) {
sharedManager.removeItemAtPath(url.path, error: nil)
}
// video writer
_videoWriter = AVAssetWriter(URL: url, fileType: AVFileTypeQuickTimeMovie, error: nil)
// writer input
var videoSettings = [AVVideoCodecKey:AVVideoCodecH264, AVVideoWidthKey:size.width, AVVideoHeightKey:size.height]
_videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
_videoWriterInput.expectsMediaDataInRealTime = true
_videoWriter.addInput(_videoWriterInput)
// pixel buffer adapter
var adapterAttributes = [kCVPixelBufferPixelFormatTypeKey:kCVPixelFormatType_32BGRA, kCVPixelBufferWidthKey: size.width,
kCVPixelBufferHeightKey: size.height,
kCVPixelFormatOpenGLESCompatibility: kCFBooleanTrue]
_adapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: _videoWriterInput, sourcePixelBufferAttributes: adapterAttributes)
var poolCreateResult:CVReturn = CVPixelBufferPoolCreatePixelBuffer(nil, _adapter.pixelBufferPool, _buffer)
println("pool creation:\(poolCreateResult)")
_videoWriter.startWriting()
_videoWriter.startSessionAtSourceTime(kCMTimeZero)
}
func addImage(image:UIImage, frameNum:Int, fps:Int)->Bool {
self.createPixelBufferFromCGImage(image.CGImage, pixelBufferPtr: _buffer)
var presentTime:CMTime = CMTimeMake(Int64(frameNum), Int32(fps))
var result:Bool = _adapter.appendPixelBuffer(_buffer.memory?.takeUnretainedValue(), withPresentationTime: presentTime)
return result
}
func finalizeMovie(timeStamp: CMTime) {
_videoWriterInput.markAsFinished()
_videoWriter.endSessionAtSourceTime(timeStamp)
_videoWriter.finishWritingWithCompletionHandler({println("video writer finished with status: \(self._videoWriter.status)")})
}
func createPixelBufferFromCGImage(image: CGImage, pixelBufferPtr: UnsafeMutablePointer<Unmanaged<CVPixelBuffer>?>) {
let width:UInt = CGImageGetWidth(image)
let height:UInt = CGImageGetHeight(image)
let imageData:CFData = CGDataProviderCopyData(CGImageGetDataProvider(image))
let options:CFDictionary = [kCVPixelBufferCGImageCompatibilityKey:NSNumber.numberWithBool(true), kCVPixelBufferCGBitmapContextCompatibilityKey:NSNumber.numberWithBool(true)]
var status:CVReturn = CVPixelBufferCreate(kCFAllocatorDefault, width, height, OSType(kCVPixelFormatType_32BGRA), options, pixelBufferPtr)
assert(status != 0,"CVPixelBufferCreate: \(status)")
var lockStatus:CVReturn = CVPixelBufferLockBaseAddress(pixelBufferPtr.memory?.takeUnretainedValue(), 0)
println("CVPixelBufferLockBaseAddress: \(lockStatus)")
var pxData:UnsafeMutablePointer<(Void)> = CVPixelBufferGetBaseAddress(pixelBufferPtr.memory?.takeUnretainedValue())
let bitmapinfo = CGBitmapInfo.fromRaw(CGImageAlphaInfo.NoneSkipFirst.toRaw())
let rgbColorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB()
var context:CGContextRef = CGBitmapContextCreate(pxData, width, height, 8, 4*CGImageGetWidth(image), rgbColorSpace, bitmapinfo!)
CGContextDrawImage(context, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), image)
CVPixelBufferUnlockBaseAddress(pixelBufferPtr.memory?.takeUnretainedValue(), 0)
}
}
2 ответа
Я не могу точно ответить на ваш вопрос, к сожалению, но я работаю над кодом, который по сути делает то же самое. И мое случилось так, что получило больше, чем ошибка, которую вы получили; он доходит до того, что пытается добавить изображения в фильм, а затем просто терпит неудачу, так и не получая успешного результата от appendPixelBuffer() - и я не уверен, как выяснить, почему. Я публикую это в надежде, что это поможет вам продвинуться дальше.
(Мой код адаптирован из AVFoundation + AssetWriter: создание фильма с изображениями и аудио, и я использовал ваш пост, чтобы помочь сориентироваться в некоторых махинациях с указателем...)
func writeAnimationToMovie(path: String, size: CGSize, animation: Animation) -> Bool {
var error: NSError?
let writer = AVAssetWriter(URL: NSURL(fileURLWithPath: path), fileType: AVFileTypeQuickTimeMovie, error: &error)
let videoSettings = [AVVideoCodecKey: AVVideoCodecH264, AVVideoWidthKey: size.width, AVVideoHeightKey: size.height]
let input = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: input, sourcePixelBufferAttributes: nil)
input.expectsMediaDataInRealTime = true
writer.addInput(input)
writer.startWriting()
writer.startSessionAtSourceTime(kCMTimeZero)
var buffer: CVPixelBufferRef
var frameCount = 0
for frame in animation.frames {
let rect = CGRectMake(0, 0, size.width, size.height)
let rectPtr = UnsafeMutablePointer<CGRect>.alloc(1)
rectPtr.memory = rect
buffer = pixelBufferFromCGImage(frame.image.CGImageForProposedRect(rectPtr, context: nil, hints: nil).takeUnretainedValue(), size)
var appendOk = false
var j = 0
while (!appendOk && j < 30) {
if pixelBufferAdaptor.assetWriterInput.readyForMoreMediaData {
let frameTime = CMTimeMake(Int64(frameCount), 10)
appendOk = pixelBufferAdaptor.appendPixelBuffer(buffer, withPresentationTime: frameTime)
// appendOk will always be false
NSThread.sleepForTimeInterval(0.05)
} else {
NSThread.sleepForTimeInterval(0.1)
}
j++
}
if (!appendOk) {
println("Doh, frame \(frame) at offset \(frameCount) failed to append")
}
}
input.markAsFinished()
writer.finishWritingWithCompletionHandler({
if writer.status == AVAssetWriterStatus.Failed {
println("oh noes, an error: \(writer.error.description)")
} else {
println("hrmmm, there should be a movie?")
}
})
return true;
}
куда pixelBufferFromCGImage
определяется так:
func pixelBufferFromCGImage(image: CGImageRef, size: CGSize) -> CVPixelBufferRef {
let options = [
kCVPixelBufferCGImageCompatibilityKey: true,
kCVPixelBufferCGBitmapContextCompatibilityKey: true]
var pixBufferPointer = UnsafeMutablePointer<Unmanaged<CVPixelBuffer>?>.alloc(1)
let status = CVPixelBufferCreate(
nil,
UInt(size.width), UInt(size.height),
OSType(kCVPixelFormatType_32ARGB),
options,
pixBufferPointer)
CVPixelBufferLockBaseAddress(pixBufferPointer.memory?.takeUnretainedValue(), 0)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapinfo = CGBitmapInfo.fromRaw(CGImageAlphaInfo.NoneSkipFirst.toRaw())
var pixBufferData:UnsafeMutablePointer<(Void)> = CVPixelBufferGetBaseAddress(pixBufferPointer.memory?.takeUnretainedValue())
let context = CGBitmapContextCreate(
pixBufferData,
UInt(size.width), UInt(size.height),
8, UInt(4 * size.width),
rgbColorSpace, bitmapinfo!)
CGContextConcatCTM(context, CGAffineTransformMakeRotation(0))
CGContextDrawImage(
context,
CGRectMake(0, 0, CGFloat(CGImageGetWidth(image)), CGFloat(CGImageGetHeight(image))),
image)
CVPixelBufferUnlockBaseAddress(pixBufferPointer.memory?.takeUnretainedValue(), 0)
return pixBufferPointer.memory!.takeUnretainedValue()
}
В соответствии с документами для pixelBufferPool
:
Это свойство равно NULL перед первым вызовом startSessionAtTime: для связанного объекта AVAssetWriter.
Перемещение вызова на CVPixelBufferPoolCreatePixelBuffer
до конца init
следует исправить непосредственную проблему.
Несколько других наблюдений:
- У вас есть
AVAssetWriterInputPixelBufferAdaptor
настроен для BGRA, но вcreatePixelBufferFromCGImage
вы используете RGB. Ваши финальные видео будут выглядеть странно, если форматы пикселей не совпадают. - Вам не нужно звонить
CVPixelBufferCreate
в вашемcreatePixelBufferFromCGImage
метод. Это противоречит цели использования буферного пула. - Если вы выполняете это в тесном цикле, потребление памяти станет проблемой. С помощью
autoreleasepool
и быть осторожным сtakeUnretainedValue
противtakeRetainedValue
поможет.
Я опубликовал справочные реализации для Swift 1.2, 2.0 и 3.0, которые используют буферные пулы.