My app takes a snapshot of the view and saves it as an image so the user can generate a video using that image and some audio I followed the tutorials
https://img.ly/blog/how-to-make-videos-from-still-images-with-avfoundation-and-swift/ http://twocentstudios.com/2017/02/20/creating-a- movie-with-an-image-and-sound-on-ios/ https://www.hackingwithswift.com/quick-start/swiftui/how-to-convert-a-swiftui-view-to-an-image
The video is successfully generated with audio and photo, but the output video is always darker than the produced photo from the snapshot.
saved image and video from the photos app
here are some features
snapshot
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self.ignoresSafeArea(.all))
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
Create video using image
func writeSingleImageToMovie(image: UIImage, movieLength: TimeInterval, outputFileURL: URL, completion: @escaping (Error?) -> ())
{
print("writeSingleImageToMovie is called")
do {
let imageSize = image.size
let videoWriter = try AVAssetWriter(outputURL: outputFileURL, fileType: AVFileType.mp4)
let videoSettings: [String: Any] = [AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: imageSize.width, //was imageSize.width
AVVideoHeightKey: imageSize.height] //was imageSize.height
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
let adaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: nil)
if !videoWriter.canAdd(videoWriterInput) { throw NSError() }
videoWriterInput.expectsMediaDataInRealTime = true
videoWriter.add(videoWriterInput)
videoWriter.startWriting()
let timeScale: Int32 = 4 // 600 recommended in CMTime for movies.
let halfMovieLength = Float64(movieLength/2.0) // videoWriter assumes frame lengths are equal.
let startFrameTime = CMTimeMake(value: 0, timescale: timeScale)
let endFrameTime = CMTimeMakeWithSeconds(Double(60), preferredTimescale: timeScale)
videoWriter.startSession(atSourceTime: startFrameTime)
guard let cgImage = image.cgImage else { throw NSError() }
let buffer: CVPixelBuffer = try CGImage.pixelBuffer(fromImage: cgImage, size: imageSize)
while !adaptor.assetWriterInput.isReadyForMoreMediaData { usleep(10) }
adaptor.append(buffer, withPresentationTime: startFrameTime)
while !adaptor.assetWriterInput.isReadyForMoreMediaData { usleep(10) }
adaptor.append(buffer, withPresentationTime: endFrameTime)
videoWriterInput.markAsFinished()
videoWriter.finishWriting
{
completion(videoWriter.error)
}
} catch {
print("CATCH Error in writeSingleImageToMovie")
completion(error)
}
}
Here is the function to create CVPixelBuffer, I tried to create buffer using CIImage also but got the same result
extension CGImage {
static func pixelBuffer(fromImage image: CGImage, size: CGSize) throws -> CVPixelBuffer {
print("pixelBuffer from CGImage")
let options: CFDictionary = [kCVPixelBufferCGImageCompatibilityKey as String: true, kCVPixelBufferCGBitmapContextCompatibilityKey as String: true] as CFDictionary
var pxbuffer: CVPixelBuffer? = nil
let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(size.width), Int(size.height), kCVPixelFormatType_32ARGB, options, &pxbuffer)
guard let buffer = pxbuffer, status == kCVReturnSuccess else { throw NSError() }
CVPixelBufferLockBaseAddress(buffer, [])
guard let pxdata = CVPixelBufferGetBaseAddress(buffer)
else { throw NSError() }
let bytesPerRow = CVPixelBufferGetBytesPerRow(buffer)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
guard let context = CGContext(data: pxdata, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) else { print("error in `CG context")
throw NSError() }
context.concatenate(CGAffineTransform(rotationAngle: 0))
context.draw(image, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
CVPixelBufferUnlockBaseAddress(buffer, [])
return buffer
}
}
I got stuck on this problem, I can’t seem to find a solution.. any tips would be appreciated.