Skip to content

Instantly share code, notes, and snippets.

@dneprDroid
Last active July 13, 2023 14:43
Show Gist options
  • Save dneprDroid/3beda7dac207785ed176911348f68441 to your computer and use it in GitHub Desktop.
Save dneprDroid/3beda7dac207785ed176911348f68441 to your computer and use it in GitHub Desktop.
VideoPreviewView - video capture and preview (swift 3)
// swift 3
import UIKit
import AVFoundation
class VideoPreviewView: UIView {
private var videoOutput: AVCaptureMovieFileOutput?
private var captureSession: AVCaptureSession?
private var previewLayer: AVCaptureVideoPreviewLayer?
var flashMode: AVCaptureTorchMode = .off {
willSet(mode) {
setupFlash(flashMode: mode)
}
}
weak var delegate: VideoPreviewViewDelegate?
convenience init(superView: UIView) {
self.init(frame: superView.bounds)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupPreview()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupPreview() {
self.clipsToBounds = true
self.backgroundColor = UIColor.black
checkVideoCameraPermission(onPermissionGranted: {
self.videoOutput = AVCaptureMovieFileOutput()
let captureSession = AVCaptureSession()
if let deviceInput = self.cameraDeviceInput(type: .back) {
captureSession.beginConfiguration()
captureSession.addInput(deviceInput)
captureSession.sessionPreset = AVCaptureSessionPresetHigh
captureSession.addOutput(self.videoOutput)
captureSession.commitConfiguration()
captureSession.startRunning()
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
if let previewLayer = previewLayer {
previewLayer.bounds = self.bounds
previewLayer.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
let cameraPreview = UIView()
cameraPreview.frame = self.bounds
cameraPreview.layer.addSublayer(previewLayer)
self.addSubview(cameraPreview)
self.captureSession = captureSession
self.previewLayer = previewLayer
}
}
})
}
override func didMoveToWindow() {
super.didMoveToWindow()
captureSession?.startRunning()
}
override func layoutSubviews() {
super.layoutSubviews()
if let previewLayer = previewLayer {
previewLayer.frame = self.bounds
if previewLayer.connection.isVideoOrientationSupported {
previewLayer.connection.videoOrientation = getAVCaptureVideoOrientation(UIDevice.current.orientation)
}
}
}
private func getAVCaptureVideoOrientation( _ orientation: UIDeviceOrientation) -> AVCaptureVideoOrientation {
switch orientation {
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
case .landscapeLeft:
return .landscapeRight
case .landscapeRight:
return .landscapeLeft
default:
return .portrait
}
}
private func setupFlash(flashMode: AVCaptureTorchMode) {
if let avDevice = (captureSession?.inputs.first as? AVCaptureDeviceInput)?.device {
if avDevice.hasTorch {
do {
try avDevice.lockForConfiguration()
} catch {
print("camera flash setup error")
}
if avDevice.torchMode != flashMode {
avDevice.torchMode = flashMode
}
avDevice.unlockForConfiguration()
}
}
}
func isBackCamera() -> Bool {
return inputCameraType() == .back
}
func isFrontCamera() -> Bool {
return inputCameraType() == .front
}
func toggleCameraType() {
guard let inputCameraType = inputCameraType() else {
return
}
if let currentInput = captureSession?.inputs.first as? AVCaptureInput? {
captureSession?.removeInput(currentInput)
}
switch inputCameraType {
case .back:
self.captureSession?.addInput(cameraDeviceInput(type: .front))
break
case .front:
self.captureSession?.addInput(cameraDeviceInput(type: .back))
break
default:
break
}
}
func releasePreview() {
if let videoOutput = videoOutput, videoOutput.isRecording {
videoOutput.stopRecording()
}
captureSession?.stopRunning()
captureSession?.removeOutput(videoOutput)
self.removeFromSuperview()
}
private func inputCameraType() -> AVCaptureDevicePosition? {
return (captureSession?.inputs.first as? AVCaptureDeviceInput)?.device.position
}
func checkVideoCameraPermission(onPermissionGranted callback: @escaping () -> Void) {
if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == .authorized {
callback()
} else {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted :Bool) -> Void in
if granted {
DispatchQueue.main.async {
callback()
}
} else {
print("camera permission error...")
}
});
}
}
private func cameraDeviceInput(type: AVCaptureDevicePosition) -> AVCaptureDeviceInput? {
let deviceTypes = [ AVCaptureDeviceType.builtInDualCamera, AVCaptureDeviceType.builtInWideAngleCamera ]
if let devices = AVCaptureDeviceDiscoverySession(deviceTypes: deviceTypes, mediaType: AVMediaTypeVideo, position: type).devices {
for device in devices where device.position == type {
return try? AVCaptureDeviceInput(device: device)
}
}
return nil
}
private func createTempFileURL() -> URL {
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true).last
let pathURL = NSURL.fileURL(withPath: path!)
let fileURL = pathURL.appendingPathComponent("movie-\(NSDate.timeIntervalSinceReferenceDate).mov")
print(" video url: \(fileURL)")
return fileURL
}
func startCaptureVideo(){
let outFileUrl = createTempFileURL()
videoOutput?.startRecording(toOutputFileURL: outFileUrl, recordingDelegate: self)
}
func stopCaptureVideo() {
videoOutput?.stopRecording()
}
deinit {
self.delegate = nil
}
}
extension VideoPreviewView: AVCaptureFileOutputRecordingDelegate {
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!,
fromConnections connections: [Any]!, error: Error!) {
if error != nil {
print("\n\n video save error \(error).......")
} else {
delegate?.onVideoSaved(outputUrl: outputFileURL)
print("video saved, outputFileURL: \(outputFileURL)")
}
}
}
protocol VideoPreviewViewDelegate: class {
func onVideoSaved(outputUrl: URL)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment