ios - maker - para que sirve imovie
Captura de vĂdeo con AVFoundation (3)
Basado en la increíble respuesta de @gwinyai, hice un marco de cámara similar. https://github.com/eonist/HybridCamera Esto también es compatible con la toma de fotos, y no tiene el problema de interferencia descrito por @Maksim Kniazev que fue causado por esta línea:
if (connection?.isVideoStabilizationSupported)! {
connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
}
He estado buscando en Stack y he encontrado preguntas similares a esto, pero ninguna me ha funcionado. Soy un principiante completo para Swift 3.0. Esencialmente, lo que estoy tratando de hacer es grabar un video usando AVFoundation. Hasta ahora he logrado capturar una imagen fija, y este es el código que tengo hasta ahora
func beginSession() {
do {
let deviceInput = try AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
if captureSession.inputs.isEmpty {
self.captureSession.addInput(deviceInput)
}
stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
if captureSession.canAddOutput(stillImageOutput) {
captureSession.addOutput(stillImageOutput)
}
}
catch {
print("error: /(error.localizedDescription)")
}
guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {
print("no preview layer")
return
}
self.view.layer.addSublayer(previewLayer)
previewLayer.frame = self.view.layer.frame
captureSession.startRunning()
// Subviews
self.view.addSubview(imgOverlay)
self.view.addSubview(blur)
self.view.addSubview(label)
self.view.addSubview(Flip)
self.view.addSubview(btnCapture)
}
y
// SAVE PHOTO
func saveToCamera() {
if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
if let cameraImage = UIImage(data: imageData) {
self.flippedImage = UIImage(cgImage: cameraImage.cgImage!, scale: cameraImage.scale, orientation: UIImageOrientation.rightMirrored)
UIImageWriteToSavedPhotosAlbum(self.flippedImage, nil, nil, nil)
}
}
})
}
}
De la respuesta de @gwinyai actualizada a la última versión de (swift 4)
import UIKit
import AVFoundation
class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
@IBOutlet weak var camPreview: UIView!
let captureSession = AVCaptureSession()
let movieOutput = AVCaptureMovieFileOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!
var outputURL: URL!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if setupSession() {
setupPreview()
startSession()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupPreview() {
// Configure previewLayer
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = camPreview.bounds
previewLayer.videoGravity = .resizeAspectFill
camPreview.layer.addSublayer(previewLayer)
}
//MARK:- Setup Camera
func setupSession() -> Bool {
captureSession.sessionPreset = AVCaptureSession.Preset.high
// Setup Camera
let camera = AVCaptureDevice.default(for: .video)
do {
let input = try AVCaptureDeviceInput(device: camera!)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
activeInput = input
}
} catch {
print("Error setting device video input: /(error)")
return false
}
// Setup Microphone
let microphone = AVCaptureDevice.default(for: .audio)
do {
let micInput = try AVCaptureDeviceInput(device: microphone!)
if captureSession.canAddInput(micInput) {
captureSession.addInput(micInput)
}
} catch {
print("Error setting device audio input: /(error)")
return false
}
// Movie output
if captureSession.canAddOutput(movieOutput) {
captureSession.addOutput(movieOutput)
}
return true
}
func setupCaptureMode(_ mode: Int) {
// Video Mode
}
//MARK:- Camera Session
func startSession() {
if !captureSession.isRunning {
videoQueue().async {
self.captureSession.startRunning()
}
}
}
func stopSession() {
if captureSession.isRunning {
videoQueue().async {
self.captureSession.stopRunning()
}
}
}
func videoQueue() -> DispatchQueue {
return DispatchQueue.main
}
func currentVideoOrientation() -> AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation
switch UIDevice.current.orientation {
case .portrait:
orientation = AVCaptureVideoOrientation.portrait
case .landscapeRight:
orientation = AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
orientation = AVCaptureVideoOrientation.portraitUpsideDown
default:
orientation = AVCaptureVideoOrientation.landscapeRight
}
return orientation
}
func startCapture() {
startRecording()
}
//EDIT 1: I FORGOT THIS AT FIRST
func tempURL() -> URL? {
let directory = NSTemporaryDirectory() as NSString
if directory != "" {
let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
return URL(fileURLWithPath: path)
}
return nil
}
func startRecording() {
if movieOutput.isRecording == false {
let connection = movieOutput.connection(with: .video)
if (connection?.isVideoOrientationSupported)! {
connection?.videoOrientation = currentVideoOrientation()
}
if (connection?.isVideoStabilizationSupported)! {
connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
}
let device = activeInput.device
if (device.isSmoothAutoFocusSupported) {
do {
try device.lockForConfiguration()
device.isSmoothAutoFocusEnabled = false
device.unlockForConfiguration()
} catch {
print("Error setting configuration: /(error)")
}
}
//EDIT2: And I forgot this
outputURL = tempURL()
movieOutput.startRecording(to: outputURL, recordingDelegate: self)
}
else {
stopRecording()
}
}
func stopRecording() {
if movieOutput.isRecording == true {
movieOutput.stopRecording()
}
}
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if (error != nil) {
print("Error recording movie: /(error!.localizedDescription)")
} else {
_ = outputURL as URL
}
outputURL = nil
}
}
Voy a facilitarle la tarea publicando todo el código que necesita para hacer una grabadora de video en AVFoundation. Este código debería funcionar si simplemente lo copia y lo pega como está. Lo único que debe recordar es que necesita conectar la salida camPreview a una UIView en el ViewController en StoryBoard. Esta vista debe ocupar todo el contenido de la pantalla. Seguiré con una explicación del código para que pueda investigar y modificar la grabadora de video para que se ajuste a las necesidades de su aplicación. También deberá asegurarse de adjuntar los permisos de privacidad pertinentes a info.plist, que son Privacidad - Descripción del uso del micrófono y Privacidad - Descripción del uso de la cámara, de lo contrario, solo verá una pantalla en negro.
NOTA: Justo en la parte inferior, he agregado cómo reproducir el video grabado bajo el título "Reproducción del video grabado".
EDITAR - Olvidé dos cosas que lo hicieron fallar durante la grabación, pero las he agregado ahora.
Swift 4
import UIKit
import AVFoundation
class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
@IBOutlet weak var camPreview: UIView!
let cameraButton = UIView()
let captureSession = AVCaptureSession()
let movieOutput = AVCaptureMovieFileOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!
var outputURL: URL!
override func viewDidLoad() {
super.viewDidLoad()
if setupSession() {
setupPreview()
startSession()
}
cameraButton.isUserInteractionEnabled = true
let cameraButtonRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.startCapture))
cameraButton.addGestureRecognizer(cameraButtonRecognizer)
cameraButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
cameraButton.backgroundColor = UIColor.red
camPreview.addSubview(cameraButton)
}
func setupPreview() {
// Configure previewLayer
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = camPreview.bounds
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
camPreview.layer.addSublayer(previewLayer)
}
//MARK:- Setup Camera
func setupSession() -> Bool {
captureSession.sessionPreset = AVCaptureSession.Preset.high
// Setup Camera
let camera = AVCaptureDevice.default(for: AVMediaType.video)!
do {
let input = try AVCaptureDeviceInput(device: camera)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
activeInput = input
}
} catch {
print("Error setting device video input: /(error)")
return false
}
// Setup Microphone
let microphone = AVCaptureDevice.default(for: AVMediaType.audio)!
do {
let micInput = try AVCaptureDeviceInput(device: microphone)
if captureSession.canAddInput(micInput) {
captureSession.addInput(micInput)
}
} catch {
print("Error setting device audio input: /(error)")
return false
}
// Movie output
if captureSession.canAddOutput(movieOutput) {
captureSession.addOutput(movieOutput)
}
return true
}
func setupCaptureMode(_ mode: Int) {
// Video Mode
}
//MARK:- Camera Session
func startSession() {
if !captureSession.isRunning {
videoQueue().async {
self.captureSession.startRunning()
}
}
}
func stopSession() {
if captureSession.isRunning {
videoQueue().async {
self.captureSession.stopRunning()
}
}
}
func videoQueue() -> DispatchQueue {
return DispatchQueue.main
}
func currentVideoOrientation() -> AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation
switch UIDevice.current.orientation {
case .portrait:
orientation = AVCaptureVideoOrientation.portrait
case .landscapeRight:
orientation = AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
orientation = AVCaptureVideoOrientation.portraitUpsideDown
default:
orientation = AVCaptureVideoOrientation.landscapeRight
}
return orientation
}
@objc func startCapture() {
startRecording()
}
//EDIT 1: I FORGOT THIS AT FIRST
func tempURL() -> URL? {
let directory = NSTemporaryDirectory() as NSString
if directory != "" {
let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
return URL(fileURLWithPath: path)
}
return nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! VideoPlaybackViewController
vc.videoURL = sender as? URL
}
func startRecording() {
if movieOutput.isRecording == false {
let connection = movieOutput.connection(with: AVMediaType.video)
if (connection?.isVideoOrientationSupported)! {
connection?.videoOrientation = currentVideoOrientation()
}
if (connection?.isVideoStabilizationSupported)! {
connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
}
let device = activeInput.device
if (device.isSmoothAutoFocusSupported) {
do {
try device.lockForConfiguration()
device.isSmoothAutoFocusEnabled = false
device.unlockForConfiguration()
} catch {
print("Error setting configuration: /(error)")
}
}
//EDIT2: And I forgot this
outputURL = tempURL()
movieOutput.startRecording(to: outputURL, recordingDelegate: self)
}
else {
stopRecording()
}
}
func stopRecording() {
if movieOutput.isRecording == true {
movieOutput.stopRecording()
}
}
func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if (error != nil) {
print("Error recording movie: /(error!.localizedDescription)")
} else {
let videoRecorded = outputURL! as URL
performSegue(withIdentifier: "showVideo", sender: videoRecorded)
}
}
}
Swift 3
import UIKit
import AVFoundation
class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
@IBOutlet weak var camPreview: UIView!
let cameraButton = UIView()
let captureSession = AVCaptureSession()
let movieOutput = AVCaptureMovieFileOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!
var outputURL: URL!
override func viewDidLoad() {
super.viewDidLoad()
if setupSession() {
setupPreview()
startSession()
}
cameraButton.isUserInteractionEnabled = true
let cameraButtonRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.startCapture))
cameraButton.addGestureRecognizer(cameraButtonRecognizer)
cameraButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
cameraButton.backgroundColor = UIColor.red
camPreview.addSubview(cameraButton)
}
func setupPreview() {
// Configure previewLayer
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = camPreview.bounds
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
camPreview.layer.addSublayer(previewLayer)
}
//MARK:- Setup Camera
func setupSession() -> Bool {
captureSession.sessionPreset = AVCaptureSessionPresetHigh
// Setup Camera
let camera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
let input = try AVCaptureDeviceInput(device: camera)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
activeInput = input
}
} catch {
print("Error setting device video input: /(error)")
return false
}
// Setup Microphone
let microphone = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
do {
let micInput = try AVCaptureDeviceInput(device: microphone)
if captureSession.canAddInput(micInput) {
captureSession.addInput(micInput)
}
} catch {
print("Error setting device audio input: /(error)")
return false
}
// Movie output
if captureSession.canAddOutput(movieOutput) {
captureSession.addOutput(movieOutput)
}
return true
}
func setupCaptureMode(_ mode: Int) {
// Video Mode
}
//MARK:- Camera Session
func startSession() {
if !captureSession.isRunning {
videoQueue().async {
self.captureSession.startRunning()
}
}
}
func stopSession() {
if captureSession.isRunning {
videoQueue().async {
self.captureSession.stopRunning()
}
}
}
func videoQueue() -> DispatchQueue {
return DispatchQueue.main
}
func currentVideoOrientation() -> AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation
switch UIDevice.current.orientation {
case .portrait:
orientation = AVCaptureVideoOrientation.portrait
case .landscapeRight:
orientation = AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
orientation = AVCaptureVideoOrientation.portraitUpsideDown
default:
orientation = AVCaptureVideoOrientation.landscapeRight
}
return orientation
}
func startCapture() {
startRecording()
}
//EDIT 1: I FORGOT THIS AT FIRST
func tempURL() -> URL? {
let directory = NSTemporaryDirectory() as NSString
if directory != "" {
let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
return URL(fileURLWithPath: path)
}
return nil
}
func startRecording() {
if movieOutput.isRecording == false {
let connection = movieOutput.connection(withMediaType: AVMediaTypeVideo)
if (connection?.isVideoOrientationSupported)! {
connection?.videoOrientation = currentVideoOrientation()
}
if (connection?.isVideoStabilizationSupported)! {
connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
}
let device = activeInput.device
if (device?.isSmoothAutoFocusSupported)! {
do {
try device?.lockForConfiguration()
device?.isSmoothAutoFocusEnabled = false
device?.unlockForConfiguration()
} catch {
print("Error setting configuration: /(error)")
}
}
//EDIT2: And I forgot this
outputURL = tempURL()
movieOutput.startRecording(toOutputFileURL: outputURL, recordingDelegate: self)
}
else {
stopRecording()
}
}
func stopRecording() {
if movieOutput.isRecording == true {
movieOutput.stopRecording()
}
}
func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
}
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
if (error != nil) {
print("Error recording movie: /(error!.localizedDescription)")
} else {
_ = outputURL as URL
}
outputURL = nil
}
}
Así es como debe tener configurado su controlador de vista
Permisos para su Info.plist
Configuración de los delegados de grabación
Debe cumplir con AVCaptureFileOutputRecordingDelegate. Según los documentos de Apple, define una interfaz para los delegados de AVCaptureFileOutput para responder a los eventos que ocurren en el proceso de grabación de un solo archivo. Viene con dos métodos que necesita implementar y estos son los dos últimos métodos al final del código. El primero es,
func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
}
Puede agregar cualquier lógica a esto cuando el video comience a grabar. En el ejemplo de código que he dado, el video comienza a grabarse cuando tocas el botón cuadrado rojo en la esquina izquierda. El segundo es
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
if (error != nil) {
print("Error recording movie: /(error!.localizedDescription)")
} else {
_ = outputURL as URL
}
outputURL = nil
}
Esto se llama cuando el video ha terminado de grabar. En el ejemplo de código que le he dado, el video se detiene después de tocar el botón cuadrado rojo por segunda vez. Cuando el video ha dejado de grabar, obtienes una URL de archivo de salida. Esto representa su video. Puede usar esto para tal vez pasar a otro controlador de vista para reproducir el video en un AVPlayer. O puedes guardarlo. En este ejemplo, notarás que no he hecho mucho con la URL de salida.
Para comenzar a grabar un video, he usado un botón creado mediante programación que aparece como un cuadrado rojo en la esquina izquierda y responde a un UITapGesture. Puedes hacer un mejor botón en tu aplicación.
Configuración de la sesión
El grabador de video necesita una sesión de captura que haya configurado en setupSession (). Aquí agrega los dispositivos de entrada AVCapture que incluyen la cámara y el micrófono. Según Apple, AVCaptureDeviceInput es una subclase concreta de AVCaptureInput que se utiliza para capturar datos de un objeto AVCaptureDevice. Sin embargo, el usuario debe otorgarle el acceso para usarlos, por lo que en su lista de información debe agregar Privacidad - Descripción del uso del micrófono y Privacidad - Descripción del uso de la cámara y explicar el motivo por el que desea utilizar la grabadora de video y el micrófono. Si no lo haces, solo obtendrás una pantalla negra. El valor predeterminado de la sesión es un valor constante que indica el nivel de calidad o la tasa de bits de la salida. He establecido esto en alto, pero hay otras opciones que puedes explorar. El movieOutput es del tipo AVCaptureMovieFileOutput que, según Apple, es una subclase concreta de AVCaptureFileOutput que se utiliza para capturar datos en una película QuickTime. Esto es lo que realmente le permite grabar y guardar el video.
Configuración de la vista previa
Aquí es donde se configura la capa de vista previa de la cámara que se realiza en setupPreview (). Configura la capa de vista previa con la sesión de captura que ha creado con el siguiente AVCaptureVideoPreviewLayer (session: captureSession).
Comenzando la sesión
El paso final es comenzar la sesión que se realiza en startSession (). Comprueba si una sesión ya se está ejecutando y si no lo está, entonces inicia una.
if !captureSession.isRunning {
videoQueue().async {
self.captureSession.startRunning()
}
}
Comenzando la grabación
Cuando toca el botón rojo, se llama al método startRecording (). Aquí he añadido métodos para manejar la orientación y estabilización de video. Finalmente, vemos nuevamente la variable movieOutput que configuramos anteriormente con nuestra sesión. Lo llamamos para grabar nuestra película a outputURL y le decimos que nuestros métodos de delegado para manejar el inicio y el final de la grabación están en el mismo controlador de vista (esos dos últimos métodos).
Para de grabar
Da la casualidad de que cuando vuelves a tocar el botón rojo, se inicia nuevamente la llamada a StartRecoding, pero notará que se está grabando algo y se llama stopRecording.
Reproducción del video grabado
Hoy estoy siendo generoso, así que voy a lanzar esto también.
Crea un nuevo controlador de vista y llámalo VideoPlayback. Conéctalo con tu primer ViewController usando un segue en Storyboard. Asigne al segue un identificador de "showVideo". Crea una vista en UIV y llena la pantalla de VideoPlayback y crea una salida a su controlador de vista llamado videoView. Agregue el siguiente código a su nuevo controlador de vista VideoPlayback:
Swift 4
import UIKit
import AVFoundation
class VideoPlayback: UIViewController {
let avPlayer = AVPlayer()
var avPlayerLayer: AVPlayerLayer!
var videoURL: URL!
//connect this to your uiview in storyboard
@IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = view.bounds
avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoView.layer.insertSublayer(avPlayerLayer, at: 0)
view.layoutIfNeeded()
let playerItem = AVPlayerItem(url: videoURL as URL)
avPlayer.replaceCurrentItem(with: playerItem)
avPlayer.play()
}
}
Swift 3
import UIKit
import AVFoundation
class VideoPlayback: UIViewController {
let avPlayer = AVPlayer()
var avPlayerLayer: AVPlayerLayer!
var videoURL: URL!
//connect this to your uiview in storyboard
@IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = view.bounds
avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
videoView.layer.insertSublayer(avPlayerLayer, at: 0)
view.layoutIfNeeded()
let playerItem = AVPlayerItem(url: videoURL as URL)
avPlayer.replaceCurrentItem(with: playerItem)
avPlayer.play()
}
}
Ahora vuelva a su último método de delegado y modifíquelo de la siguiente manera:
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
if (error != nil) {
print("Error recording movie: /(error!.localizedDescription)")
} else {
let videoRecorded = outputURL! as URL
performSegue(withIdentifier: "showVideo", sender: videoRecorded)
}
}
Finalmente, cree un método de preparación para segue que inicializará el videoURL que se jugará con el AVPlayer.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! VideoPlayback
vc.videoURL = sender as! URL
}
Ahora para probar, vuelve y comienza a grabar un video. En el segundo toque del cuadrado rojo, se realizará la segue y verá que el video grabado se está reproduciendo automáticamente.