使用Swift从UIImage构建video

我正在构建一个可以实时获取视图的IP摄像头应用程序,现在我想使用Swift录制MJPEG格式的video。

let imageData = receivedData , imageData.length > 0, let receivedImage = UIImage(data: imageData as Data) 

在这里我收到的所有图像都保存为UIImage,问题是如何记录图像流? 我在Github找到了一个有用的资源,但是我失败了,链接很糟糕: https : //gist.github.com/acj/6ae90aa1ebb8cad6b47b

任何人都可以给我一些提示,或者你们有样品项目吗? 非常感谢,谢谢!

更新:我使用Amrit Tiwari的答案中的代码,但得到此错误:为640.0×640.0video创建资产编写器错误将图像转换为video:pixelBufferPool nil在启动会话后

 if let imageData = receivedData , imageData.length > 0, let receivedImage = UIImage(data: imageData as Data){ let size = CGSize(width: 640, height: 640) writeImagesAsMovie([receivedImage], videoPath: "test.mp4", videoSize: size, videoFPS: 2) } 

我不确定路径参数是否正确(我想将它保存在Documents目录中)。 请帮帮我,谢谢!

这个问题已经解决了。 我只是复制并粘贴它。

 func writeImagesAsMovie(allImages: [UIImage], videoPath: String, videoSize: CGSize, videoFPS: Int32) { // Create AVAssetWriter to write video guard let assetWriter = createAssetWriter(videoPath, size: videoSize) else { print("Error converting images to video: AVAssetWriter not created") return } // If here, AVAssetWriter exists so create AVAssetWriterInputPixelBufferAdaptor let writerInput = assetWriter.inputs.filter{ $0.mediaType == AVMediaTypeVideo }.first! let sourceBufferAttributes : [String : AnyObject] = [ kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String : videoSize.width, kCVPixelBufferHeightKey as String : videoSize.height, ] let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: sourceBufferAttributes) // Start writing session assetWriter.startWriting() assetWriter.startSessionAtSourceTime(kCMTimeZero) if (pixelBufferAdaptor.pixelBufferPool == nil) { print("Error converting images to video: pixelBufferPool nil after starting session") return } // -- Create queue for  let mediaQueue = dispatch_queue_create("mediaInputQueue", nil) // -- Set video parameters let frameDuration = CMTimeMake(1, videoFPS) var frameCount = 0 // -- Add images to video let numImages = allImages.count writerInput.requestMediaDataWhenReadyOnQueue(mediaQueue, usingBlock: { () -> Void in // Append unadded images to video but only while input ready while (writerInput.readyForMoreMediaData && frameCount < numImages) { let lastFrameTime = CMTimeMake(Int64(frameCount), videoFPS) let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration) if !self.appendPixelBufferForImageAtURL(allImages[frameCount], pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) { print("Error converting images to video: AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer") return } frameCount += 1 } // No more images to add? End video. if (frameCount >= numImages) { writerInput.markAsFinished() assetWriter.finishWritingWithCompletionHandler { if (assetWriter.error != nil) { print("Error converting images to video: \(assetWriter.error)") } else { self.saveVideoToLibrary(NSURL(fileURLWithPath: videoPath)) print("Converted images to movie @ \(videoPath)") } } } }) } func createAssetWriter(path: String, size: CGSize) -> AVAssetWriter? { // Convert  to NSURL object let pathURL = NSURL(fileURLWithPath: path) // Return new asset writer or nil do { // Create asset writer let newWriter = try AVAssetWriter(URL: pathURL, fileType: AVFileTypeMPEG4) // Define settings for video input let videoSettings: [String : AnyObject] = [ AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : size.width, AVVideoHeightKey : size.height, ] // Add video input to writer let assetWriterVideoInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings) newWriter.addInput(assetWriterVideoInput) // Return writer print("Created asset writer for \(size.width)x\(size.height) video") return newWriter } catch { print("Error creating asset writer: \(error)") return nil } } func appendPixelBufferForImageAtURL(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool { var appendSucceeded = false autoreleasepool { if let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool { let pixelBufferPointer = UnsafeMutablePointer.alloc(1) let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer( kCFAllocatorDefault, pixelBufferPool, pixelBufferPointer ) if let pixelBuffer = pixelBufferPointer.memory where status == 0 { fillPixelBufferFromImage(image, pixelBuffer: pixelBuffer) appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime) pixelBufferPointer.destroy() } else { NSLog("Error: Failed to allocate pixel buffer from pool") } pixelBufferPointer.dealloc(1) } } return appendSucceeded } func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBufferRef) { CVPixelBufferLockBaseAddress(pixelBuffer, 0) let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() // Create CGBitmapContext let context = CGBitmapContextCreate( pixelData, Int(image.size.width), Int(image.size.height), 8, CVPixelBufferGetBytesPerRow(pixelBuffer), rgbColorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue ) // Draw image into context CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage) CVPixelBufferUnlockBaseAddress(pixelBuffer, 0) } func saveVideoToLibrary(videoURL: NSURL) { PHPhotoLibrary.requestAuthorization { status in // Return if unauthorized guard status == .Authorized else { print("Error saving video: unauthorized access") return } // If here, save video to library PHPhotoLibrary.sharedPhotoLibrary().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoURL) }) { success, error in if !success { print("Error saving video: \(error)") } } } } 

如果它不解决你的问题,那么我会帮助你。

更新为Swift 4:

 // MARK: - Write Images as Movie - func writeImagesAsMovie(allImages: [UIImage], videoPath: String, videoSize: CGSize, videoFPS: Int32) { // Create AVAssetWriter to write video guard let assetWriter = createAssetWriter(path: videoPath, size: videoSize) else { print("Error converting images to video: AVAssetWriter not created") return } // If here, AVAssetWriter exists so create AVAssetWriterInputPixelBufferAdaptor let writerInput = assetWriter.inputs.filter { $0.mediaType == AVMediaType.video }.first! let sourceBufferAttributes: [String: AnyObject] = [ kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB) as AnyObject, kCVPixelBufferWidthKey as String: videoSize.width as AnyObject, kCVPixelBufferHeightKey as String: videoSize.height as AnyObject ] let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: sourceBufferAttributes) // Start writing session assetWriter.startWriting() assetWriter.startSession(atSourceTime: kCMTimeZero) if pixelBufferAdaptor.pixelBufferPool == nil { print("Error converting images to video: pixelBufferPool nil after starting session") return } // -- Create queue for  let mediaQueue = DispatchQueue.init(label: "mediaInputQueue") // -- Set video parameters let frameDuration = CMTimeMake(1, videoFPS) var frameCount = 0 // -- Add images to video let numImages = allImages.count writerInput.requestMediaDataWhenReady(on: mediaQueue, using: { () -> Void in // Append unadded images to video but only while input ready while writerInput.isReadyForMoreMediaData && frameCount < numImages { let lastFrameTime = CMTimeMake(Int64(frameCount), videoFPS) let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration) if !self.appendPixelBufferForImageAtURL(image: allImages[frameCount], pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) { print("Error converting images to video: AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer") return } frameCount += 1 } // No more images to add? End video. if frameCount >= numImages { writerInput.markAsFinished() assetWriter.finishWriting { if assetWriter.error != nil { print("Error converting images to video: \(assetWriter.error?.localizedDescription ?? "")") } else { self.saveVideoToLibrary(videoURL: URL.init(string: videoPath)!) print("Converted images to movie @ \(videoPath)") } } } }) } // MARK: - Create Asset Writer - func createAssetWriter(path: String, size: CGSize) -> AVAssetWriter? { // Convert  to NSURL object let pathURL = URL.init(fileURLWithPath: path) // Return new asset writer or nil do { // Create asset writer let newWriter = try AVAssetWriter(outputURL: pathURL, fileType: AVFileType.mp4) // Define settings for video input let videoSettings: [String: AnyObject] = [ AVVideoCodecKey: AVVideoCodecType.h264 as AnyObject, AVVideoWidthKey: size.width as AnyObject, AVVideoHeightKey: size.height as AnyObject ] // Add video input to writer let assetWriterVideoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings) newWriter.add(assetWriterVideoInput) // Return writer print("Created asset writer for \(size.width)x\(size.height) video") return newWriter } catch { print("Error creating asset writer: \(error)") return nil } } // MARK: - Append Pixel Buffer - func appendPixelBufferForImageAtURL(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool { var appendSucceeded = false autoreleasepool { if let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool { let pixelBufferPointer = UnsafeMutablePointer.allocate(capacity: 1) let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer( kCFAllocatorDefault, pixelBufferPool, pixelBufferPointer ) if let pixelBuffer = pixelBufferPointer.pointee, status == 0 { fillPixelBufferFromImage(image: image, pixelBuffer: pixelBuffer) appendSucceeded = pixelBufferAdaptor.append(pixelBuffer, withPresentationTime: presentationTime) pixelBufferPointer.deinitialize() } else { NSLog("Error: Failed to allocate pixel buffer from pool") } pixelBufferPointer.deallocate(capacity: 1) } } return appendSucceeded } // MARK: - Fill Pixel Buffer - func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBuffer) { CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() // Create CGBitmapContext let context = CGContext( data: pixelData, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue ) // Draw image into context" context?.draw(image.cgImage!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height)) CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) } // MARK: - Save Video - func saveVideoToLibrary(videoURL: URL) { PHPhotoLibrary.requestAuthorization { status in // Return if unauthorized guard status == .authorized else { print("Error saving video: unauthorized access") return } // If here, save video to library PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL) }, completionHandler: { success, error in if !success { print("Error saving video: \(error?.localizedDescription ?? "")") } }) } }