【问题标题】:CVPixelBufferPool Error ( kCVReturnInvalidArgument/-6661)CVPixelBufferPool 错误 (kCVReturnInvalidArgument/-6661)
【发布时间】:2014-09-01 18:11:38
【问题描述】:

我已经用 Swift (How to use CVPixelBufferPool in conjunction with AVAssetWriterInputPixelBufferAdaptor in iPhone?) 实现了之前的建议, 但是在按照指导使用 CVPixelBufferPoolCreatePixelBuffer 时遇到了“kCVReturnInvalidArgument”(错误值:-6661)。

我基本上是在尝试从图像创建电影,但是由于没有成功创建缓冲池,我无法附加像素缓冲区——这是我的代码。

非常感谢任何建议!

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)


}



}

【问题讨论】:

  • 我通过在 obj-c 中实现我的“createPixelBufferFromCGImage”函数并从那里调用CVPixelBufferPoolCreatePixelBuffer 解决了(或者更准确地说,解决了)这个问题。我的猜测是我们的 Swift CVPixelBuffer 指针在某种程度上与 CVPixelBufferPoolCreatePixelBuffer 不兼容,从而产生了“无效参数”错误。如果我得到一个纯 Swift 版本的工作,我会发布一个答案。

标签: swift ios8


【解决方案1】:

令人沮丧的是,我无法准确回答您的问题,但我正在编写本质上相同的代码。而且,我的碰巧比你得到的错误更进一步;它一直到尝试将图像添加到电影中的地步,然后由于从未从 appendPixelBuffer() 获得成功结果而失败——我不知道如何找出原因。不过,我发布此内容是希望它可以帮助您走得更远。

(我的代码改编自AVFoundation + AssetWriter: Generate Movie With Images and Audio,我使用您的帖子来帮助导航一些指针互操作恶作剧......)

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()
}

【讨论】:

  • 啊,我真傻。我实际上并没有在输入上调用markAsFinished,在编写器上调用finishWritingWithCompletionHandler()
  • 你有没有让这个工作?我正在做同样的事情(将工作目标 C 代码移植到 Swift)。
【解决方案2】:

根据docspixelBufferPool

在关联的 AVAssetWriter 对象上第一次调用 startSessionAtTime: 之前,此属性为 NULL。

将对CVPixelBufferPoolCreatePixelBuffer 的调用移至init 的末尾应该可以解决当前的问题。

其他一些观察:

  • 您已为 BGRA 配置了 AVAssetWriterInputPixelBufferAdaptor,但在 createPixelBufferFromCGImage 中您使用的是 RGB。如果像素格式不匹配,您的最终视频会看起来很奇怪。
  • 您不需要在createPixelBufferFromCGImage 方法中调用CVPixelBufferCreate。这违背了使用缓冲池的目的。
  • 如果您在一个紧密的循环中运行它,内存消耗将成为一个问题。使用 autoreleasepool 并小心使用 takeUnretainedValuetakeRetainedValue 会有所帮助。

我已经为使用缓冲池的 Swift 1.2、2.0 和 3.0 发布了 reference implementations

【讨论】:

  • 如果将 AVAssetWriterInputPixelBufferAdaptor 的属性设置为 nil 会怎样?其他 SO 答案似乎可以做到这一点。介意解释为什么你明确设置属性?感谢分享代码!
  • 也在你的 2.0 代码中为什么你在 requestMediaDataWhenReadyOnQueue 块内设置 frameCount = Int64(0)?这是否意味着每次调用块时 frameCount 都会重置为 0?例如,如果您写出 100 张图片,然后 readyForMoreMediaData 变为 false,那么当您再次从 101 张图片开始时,frameCount 将重置为 0 而不是 101?
  • @Crashalot 您可以使用nil 作为属性。我已经对色彩空间和不同的维度进行了一些实验,只是将代码留在了原处。 // 对于我在测试中使用的照片集,readyForMoreMediaData 始终为 true,并且不会触发您所描述的场景。不过,您是对的,它可能会出现问题,并且为了稳健起见,您可能应该在块之外保持该状态(frameCount 等)。
  • 好吧,这就是假设。那么将作者标记为已完成也应该如此?你想要一个修改版本的代码来解释这些变化吗?非常感谢分享。太棒了!同样使用nil 制作了一个只有绿屏的视频,因此您必须定义 AVAssetWriterInputPixelBufferAdaptor 属性,至少对于您的代码。
  • 已完成 Swift 版本的移植,如有问题请提出改进​​意见:stackoverflow.com/questions/3741323/…
猜你喜欢
  • 1970-01-01
  • 2014-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
  • 2015-03-25
  • 2013-05-15
相关资源
最近更新 更多