问候我正在使用FirebaseMLVission
为我的用户检测eye blink
。但是,我只能让它显示输出,但不能录制视频。通过一些研究,我得出的结论是,实现此目标的唯一方法是使用AVAssetWriterInput
。我尝试实现它,但收到[AVAssetWriter startWriting] Cannot call method when status is 3
作为错误消息。
我缩小了代码,使其长度不超过100页,如果我错过了任何内容,请告诉我。
我尝试实施此Capturing Video with AVFoundation
谢谢
https://github.com/BradLarson/GPUImage/issues/2015
AVAssetWriter queue guidance Swift 3
private let detectors: [Detector] = [.onDeviceFace, .onDeviceText]
private var currentDetector: Detector = .onDeviceFace
private var isUsingFrontCamera = true
private var previewLayer: AVCaptureVideoPreviewLayer!
private lazy var captureSession = AVCaptureSession()
private lazy var sessionQueue = DispatchQueue(label: Constant.sessionQueueLabel)
private lazy var vision = Vision.vision()
private var lastFrame: CMSampleBuffer?
fileprivate(set) lazy var isRecording = false
fileprivate var videoWriter: AVAssetWriter!
fileprivate var videoWriterInput: AVAssetWriterInput!
fileprivate var audioWriterInput: AVAssetWriterInput!
fileprivate var sessionAtSourceTime: CMTime?
private lazy var previewOverlayView: UIImageView = {
precondition(isViewLoaded)
let previewOverlayView = UIImageView(frame: .zero)
previewOverlayView.translatesAutoresizingMaskIntoConstraints = false
return previewOverlayView
}()
private lazy var annotationOverlayView: UIView = {
precondition(isViewLoaded)
let annotationOverlayView = UIView(frame: .zero)
annotationOverlayView.translatesAutoresizingMaskIntoConstraints = false
return annotationOverlayView
}()
@IBOutlet weak var cameraView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
setupWriter()
setUpPreviewOverlayView()
setUpAnnotationOverlayView()
setUpCaptureSessionOutput()
setUpCaptureSessionInput()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startSession()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
stopSession()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
previewLayer.frame = cameraView.frame
}
// MARK: - IBActions
@IBAction func RecordButton(_ sender: Any) {
start()
}
@IBAction func StopRecordingButton(_ sender: Any) {
stop()
}
fileprivate func setupWriter() {
do {
let url = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
videoWriter = try AVAssetWriter(url: url, fileType: AVFileType.mp4)
//Add video input
videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: [
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: 720,
AVVideoHeightKey: 1280,
AVVideoCompressionPropertiesKey: [
AVVideoAverageBitRateKey: 2300000,
],
])
videoWriterInput.expectsMediaDataInRealTime = true //Make sure we are exporting data at realtime
if videoWriter.canAdd(videoWriterInput) {
videoWriter.add(videoWriterInput)
}
//Add audio input
audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey: 1,
AVSampleRateKey: 44100,
AVEncoderBitRateKey: 64000,
])
audioWriterInput.expectsMediaDataInRealTime = true
if videoWriter.canAdd(audioWriterInput) {
videoWriter.add(audioWriterInput)
}
videoWriter.startWriting() //Means ready to write down the file
}
catch let error {
debugPrint(error.localizedDescription)
}
}
private func setUpCaptureSessionOutput() {
sessionQueue.async {
self.captureSession.beginConfiguration()
// When performing latency tests to determine ideal capture settings,
// run the app in 'release' mode to get accurate performance metrics
self.captureSession.sessionPreset = AVCaptureSession.Preset.medium
//let output = AVCaptureMovieFileOutput()
let output = AVCaptureVideoDataOutput()
var audioDataOutput = AVCaptureAudioDataOutput()
output.videoSettings =
[(kCVPixelBufferPixelFormatTypeKey as String): kCVPixelFormatType_32BGRA]
let outputQueue = DispatchQueue(label: Constant.videoDataOutputQueueLabel)
output.setSampleBufferDelegate(self, queue: outputQueue)
guard self.captureSession.canAddOutput(output) else {
print("Failed to add capture session output.")
return
}
self.captureSession.addOutput(output)
//self.captureSession.addOutput(self.movieOutput)
self.captureSession.commitConfiguration()
self.captureSession.startRunning()
}
}
private func setUpCaptureSessionInput() {
sessionQueue.async {
let cameraPosition: AVCaptureDevice.Position = self.isUsingFrontCamera ? .front : .back
guard let device = self.captureDevice(forPosition: cameraPosition) else {
print("Failed to get capture device for camera position: \(cameraPosition)")
return
}
do {
self.captureSession.beginConfiguration()
let currentInputs = self.captureSession.inputs
for input in currentInputs {
self.captureSession.removeInput(input)
}
let audioInput = AVCaptureDevice.default(for: AVMediaType.audio)
let input = try AVCaptureDeviceInput(device: device)
guard self.captureSession.canAddInput(input) else {
print("Failed to add capture session input.")
return
}
self.captureSession.addInput(input)
try self.captureSession.addInput(AVCaptureDeviceInput(device: audioInput!))
self.captureSession.commitConfiguration()
} catch {
print("Failed to create capture device input: \(error.localizedDescription)")
}
}
}
private func startSession() {
sessionQueue.async {
self.captureSession.startRunning()
}
}
private func stopSession() {
sessionQueue.async {
self.captureSession.stopRunning()
}
}
extension CameraTestViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(
_ output: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection
) {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer from sample buffer.")
return
}
let writable = canWrite()
if writable,
sessionAtSourceTime == nil {
//Start writing
sessionAtSourceTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
videoWriter.startSession(atSourceTime: sessionAtSourceTime!)
}
if videoWriterInput.isReadyForMoreMediaData {
//Write video buffer
videoWriterInput.append(sampleBuffer)
}
else if writable,
//captureOutput == audioDataOutput,
audioWriterInput.isReadyForMoreMediaData {
//Write audio buffer
audioWriterInput.append(sampleBuffer)
}
lastFrame = sampleBuffer
let visionImage = VisionImage(buffer: sampleBuffer)
let metadata = VisionImageMetadata()
let orientation = UIUtilities.imageOrientation(
fromDevicePosition: isUsingFrontCamera ? .front : .back
)
let visionOrientation = UIUtilities.visionImageOrientation(from: orientation)
metadata.orientation = visionOrientation
visionImage.metadata = metadata
let imageWidth = CGFloat(CVPixelBufferGetWidth(imageBuffer))
let imageHeight = CGFloat(CVPixelBufferGetHeight(imageBuffer))
switch currentDetector {
case .onDeviceFace:
detectFacesOnDevice(in: visionImage, width: imageWidth, height: imageHeight)
case .onDeviceText:
recognizeTextOnDevice(in: visionImage, width: imageWidth, height: imageHeight)
}
}
}