Last active
January 14, 2025 05:50
-
-
Save thatseeyou/caa8db15f39963dc1060 to your computer and use it in GitHub Desktop.
How to capture video frames from the camera as images using AV Foundation on iOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// | |
// Technical Q&A QA1702 | |
// How to capture video frames from the camera as images using AV Foundation on iOS | |
// | |
import UIKit | |
import AVFoundation | |
import CoreMedia | |
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate | |
{ | |
let mainGroup = UIStackView() | |
let imageView = UIImageView(frame: CGRectZero) | |
override func viewDidLoad() | |
{ | |
super.viewDidLoad() | |
// 1. Layout Views | |
view.addSubview(mainGroup) | |
mainGroup.axis = UILayoutConstraintAxis.Vertical | |
mainGroup.distribution = UIStackViewDistribution.Fill | |
mainGroup.addArrangedSubview(imageView) | |
imageView.contentMode = UIViewContentMode.ScaleAspectFit | |
// 2. Create session & configure | |
let captureSession = AVCaptureSession() | |
captureSession.sessionPreset = AVCaptureSessionPresetMedium | |
// 3. set input | |
let backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) | |
do { | |
let input = try AVCaptureDeviceInput(device: backCamera) | |
captureSession.addInput(input) | |
} | |
catch { | |
print("can't access camera") | |
return | |
} | |
// although we don't use this, it's required to get captureOutput invoked | |
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) | |
view.layer.addSublayer(previewLayer) | |
// 4. set output & configure | |
let videoOutput = AVCaptureVideoDataOutput() | |
if captureSession.canAddOutput(videoOutput) { | |
captureSession.addOutput(videoOutput) | |
} | |
let queue = dispatch_queue_create("sample buffer delegate", nil) | |
videoOutput.setSampleBufferDelegate(self, queue: queue) | |
// 5. Specify the pixel format | |
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey : UInt(kCVPixelFormatType_32BGRA)] | |
// 6. start running session | |
captureSession.startRunning() | |
} | |
override func viewDidLayoutSubviews() | |
{ | |
let topMargin = topLayoutGuide.length | |
mainGroup.frame = CGRect(x: 0, y: topMargin, width: view.frame.width, height: view.frame.height - topMargin).insetBy(dx: 5, dy: 5) | |
} | |
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) | |
{ | |
let image = imageFromSampleBuffer(sampleBuffer) | |
dispatch_async(dispatch_get_main_queue()) | |
{ | |
print(image.CGImage) | |
self.imageView.image = image | |
} | |
} | |
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage { | |
let imageBuffer:CVImageBuffer! = CMSampleBufferGetImageBuffer(sampleBuffer) | |
CVPixelBufferLockBaseAddress(imageBuffer, 0) | |
let baseAddress: UnsafeMutablePointer<Void> = CVPixelBufferGetBaseAddress(imageBuffer) | |
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) | |
let width = CVPixelBufferGetWidth(imageBuffer) | |
let height = CVPixelBufferGetHeight(imageBuffer) | |
let colorSpace = CGColorSpaceCreateDeviceRGB(); | |
// Create a bitmap graphics context with the sample buffer data | |
let context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue); | |
let quartzImage = CGBitmapContextCreateImage(context) | |
CVPixelBufferUnlockBaseAddress(imageBuffer, 0) | |
// Create an image object from the Quartz image | |
let image = UIImage(CGImage:quartzImage!); | |
return (image); | |
} | |
func colorForPixel(image: UIImage, x: Int, y: Int) -> UIColor { | |
let cgImage = image.CGImage | |
let imageWidth = CGImageGetWidth(cgImage) | |
let imageHeight = CGImageGetHeight(cgImage) | |
let bytesPerPixel = CGImageGetBitsPerPixel(cgImage) / 8 | |
//print("\(imageWidth) x \(imageHeight) x \(bytesPerPixel)") | |
let pixelData:CFData! = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage)); | |
let data:UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) | |
// CGImage is flipped. So (imageHeight - y) is needed. | |
let pixelValue = data + (imageWidth * (imageHeight - y) + x) * bytesPerPixel | |
// kCVPixelFormatType_32BGRA | |
let blue = CGFloat(pixelValue[0]) | |
let green = CGFloat(pixelValue[1]) | |
let red = CGFloat(pixelValue[2]) | |
let alpha = CGFloat(pixelValue[3]) | |
return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: alpha / 255) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment