Streaming Audio stops after SFSpeechRecognitionTask starts - swift

I'm streaming audio file and it works perfectly and I'm also doing speech recognition task and it also works perfectly. But when the below scenario occurs it stops streaming audio file.
I start speech recognition it detects speech perfectly
Now i start streaming the audio file it does not play audio after that.
Below is my code in ViewController.m
//
// ViewController.swift
// RecordAndPlay
//
// Created by Obaid on 8/15/17.
// Copyright © 2017 test. All rights reserved.
//
import UIKit
import AVFoundation
import AudioToolbox
import Speech
class ViewController: UIViewController,SFSpeechRecognizerDelegate {
#IBOutlet var imgBackground: UIImageView!
#IBOutlet var lblTextValue: UILabel!
#IBOutlet var btnPlay: UIButton!
#IBOutlet var btnRepeatIt: UIButton!
#IBOutlet var microphoneButton: UIButton!
var stringValue: String!
var indexValue=0
var playButton:UIButton?
var player: AVPlayer! = nil
var playerItem:AVPlayerItem?
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))!
var recordButton: UIButton!
var recordingSession: AVAudioSession!
var audioRecorder: AVAudioRecorder!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
var arrayOfStrings: [String] = ["good morning", "good evening", "good night", "good afternoon", "well developed"]
var arrayOfStringsURL: [String] = ["https://storage.googleapis.com/abdul-sample-bucket/test/sampl1.mp3", "http://dictionary.cambridge.org/media/english/us_pron/u/usc/uscld/uscld00580.mp3", "http://dictionary.cambridge.org/media/english/us_pron/u/usc/uscld/uscld00588.mp3", "http://dictionary.cambridge.org/media/english/us_pron/u/usc/uscld/uscld00579.mp3", "http://dictionary.cambridge.org/media/english/uk_pron/u/ukw/ukwel/ukwelde019.mp3"]
override func viewDidLoad() {
super.viewDidLoad()
initializeSpeechRecognition()
// Do any additional setup after loading the view, typically from a nib.
//createLabels()
check_record_permission()
lblTextValue.text=arrayOfStrings[0]
imgBackground.backgroundColor=UIColor.yellow
imgBackground.frame = CGRect(x: 0, y: 140, width: self.view.frame.width, height: self.imgBackground.frame.height)
btnPlay.setImage(UIImage(named: "play.png"), for: .normal)
let xOrigin=self.view.frame.width - 70
btnPlay.frame=CGRect(x:xOrigin, y: self.btnPlay.frame.origin.y, width:self.btnPlay.frame.width, height: self.btnPlay.frame.height)
btnRepeatIt.isHidden=true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var btnClicked=0
#IBAction func start_play(_ sender: UIButton){
let url = URL(string: arrayOfStringsURL[indexValue])
print(arrayOfStringsURL[indexValue])
let playerItem:AVPlayerItem = AVPlayerItem(url: url!)
player=nil
player = AVPlayer(playerItem: playerItem)
player!.play()
imgBackground.backgroundColor=UIColor.green
lblTextValue.textColor=UIColor.white
lblTextValue.text="Repeat it!"
btnClicked=1
microphoneButton.isHidden=false
microphoneButton.frame=btnPlay.frame
microphoneButton.setImage(UIImage(named: "record.png"), for: .normal)
btnPlay.isHidden=true
btnRepeatIt.isHidden=false
}
#IBAction func repeatTheSound(_ sender: UIButton){
print("repeat the sound")
let url = URL(string: arrayOfStringsURL[1])
let playerItem:AVPlayerItem = AVPlayerItem(url: url!)
player=nil
player = AVPlayer(playerItem: playerItem)
player!.play()
}
func initializeSpeechRecognition(){
microphoneButton.isHidden=true
speechRecognizer.delegate = self
SFSpeechRecognizer.requestAuthorization { (authStatus) in
var isButtonEnabled = false
switch authStatus {
case .authorized:
isButtonEnabled = true
case .denied:
isButtonEnabled = false
print("User denied access to speech recognition")
case .restricted:
isButtonEnabled = false
print("Speech recognition restricted on this device")
case .notDetermined:
isButtonEnabled = false
print("Speech recognition not yet authorized")
}
OperationQueue.main.addOperation() {
if(self.btnClicked==1){
self.btnPlay.isEnabled = isButtonEnabled
}
}
}
}
var isAudioRecordingGranted: Bool!
func check_record_permission()
{
switch AVAudioSession.sharedInstance().recordPermission() {
case AVAudioSessionRecordPermission.granted:
isAudioRecordingGranted = true
break
case AVAudioSessionRecordPermission.denied:
isAudioRecordingGranted = false
break
case AVAudioSessionRecordPermission.undetermined:
AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
self.isAudioRecordingGranted = true
} else {
self.isAudioRecordingGranted = false
}
}
}
break
default:
break
}
}
//Speech Recognition
#IBAction func microphoneTapped(_ sender: AnyObject) {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
} else {
startRecording()
}
}
func startRecording() {
if recognitionTask != nil { //1
recognitionTask?.cancel()
recognitionTask = nil
}
let audioSession = AVAudioSession.sharedInstance() //2
do {
try audioSession.setCategory(AVAudioSessionCategoryRecord)
try audioSession.setMode(AVAudioSessionModeMeasurement)
try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't set because of an error.")
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest() //3
guard let inputNode = audioEngine.inputNode else {
fatalError("Audio engine has no input node")
} //4
guard let recognitionRequest = recognitionRequest else {
fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
} //5
recognitionRequest.shouldReportPartialResults = true //6
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in //7
var isFinal = false //8
if result != nil {
self.stringValue = result?.bestTranscription.formattedString //9
isFinal = (result?.isFinal)!
if(isFinal){
self.stringValue = self.stringValue.lowercased()
print(self.stringValue)
if(self.stringValue.contains(self.arrayOfStrings[0])){
self.imgBackground.backgroundColor=UIColor.green
self.lblTextValue.text="Well Done"
self.stringValue=""
self.updatePage()
}
else{
self.imgBackground.backgroundColor=UIColor.red
self.lblTextValue.text="Wrong! Try Again"
self.stringValue=""
}
}
self.stopRecording()
}
if error != nil || isFinal { //10
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
}
})
let recordingFormat = inputNode.outputFormat(forBus: 0) //11
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare() //12
do {
try audioEngine.start()
} catch {
print("audioEngine couldn't start because of an error.")
}
}
func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
microphoneButton.isEnabled = true
} else {
microphoneButton.isEnabled = false
}
}
func updatePage(){
print(indexValue)
if(indexValue<self.arrayOfStrings.count){
indexValue += 1
self.imgBackground.backgroundColor=UIColor.yellow
self.lblTextValue.textColor=UIColor.black
self.lblTextValue.text=self.arrayOfStrings[indexValue]
self.btnPlay.isHidden=false
self.microphoneButton.isHidden=true
}
else{
indexValue=0
}
}
func stopRecording(){
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
self.recognitionTask = nil
}
}
}
startRecording() method is when I start the speechRecognition
start_play() method is when I play the audio
something is going in the background that needs to be stopped after i start speechRecognition.

Related

Swift - Visionkit How to edit the color of the buttons "Keep Scan" and "Retake"

I have a problem.
I've implemented the visionkit framework in order to detect a text when the camera is used.
The problem is that the colors of the buttons: "Keep Scan" and "Retake" are blue. I want them in black color. How can I fix this problem? I paste my code.
Thank you in advance!
import UIKit
import Vision
import VisionKit
class ScanText: UIViewController, VNDocumentCameraViewControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var TextScan: UITextView!
#IBOutlet weak var buttonStartShape: UIButton!
#IBOutlet weak var infoScattaUnaFoto: UILabel!
#IBOutlet weak var copyButtonShape: UIButton!
var textRecognitionRequest = VNRecognizeTextRequest(completionHandler: nil)
private let textRecognitionWorkQueue = DispatchQueue(label: "MyVisionScannerQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
var classView = ViewController()
var arrayText = String()
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.cornerRadius = 15
imageView.clipsToBounds = true
TextScan.layer.cornerRadius = 15
TextScan.clipsToBounds = true
copyButtonShape.layer.cornerRadius = 25
copyButtonShape.clipsToBounds = true
buttonStartShape.layer.cornerRadius = 25
buttonStartShape.clipsToBounds = true
TextScan.isEditable = true
setupVision()
}
#IBAction func TakePicture(_ sender: Any) {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
present(scannerViewController, animated: true)
}
private func setupVision() {
textRecognitionRequest = VNRecognizeTextRequest { (request, error) in
guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
var detectedText = ""
for observation in observations {
guard let topCandidate = observation.topCandidates(2).first else { return }
print("text \(topCandidate.string) has confidence \(topCandidate.confidence)")
detectedText += topCandidate.string
detectedText += "\n"
}
DispatchQueue.main.async {
self.TextScan.isHidden = false
self.infoScattaUnaFoto.isHidden = true
self.copyButtonShape.isHidden = false
self.buttonStartShape.setTitle("Ripeti", for: .normal)
self.TextScan.text += detectedText
self.TextScan.flashScrollIndicators()
}
}
textRecognitionRequest.recognitionLevel = .accurate
}
private func processImage(_ image: UIImage) {
imageView.image = image
recognizeTextInImage(image)
}
private func recognizeTextInImage(_ image: UIImage) {
guard let cgImage = image.cgImage else { return }
TextScan.text = ""
textRecognitionWorkQueue.async {
let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
do {
try requestHandler.perform([self.textRecognitionRequest])
} catch {
print(error)
}
}
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
/* guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}*/
for i in 0 ..< scan.pageCount {
let img = scan.imageOfPage(at: i)
processImage(img)
}
let originalImage = scan.imageOfPage(at: 0)
print(originalImage)
//let newImage = compressedImage(originalImage)
controller.dismiss(animated: true)
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
//MARK: By tapping on this button, I pass all the data.
#IBAction func CopyText(_ sender: Any) {
let vc = (storyboard?.instantiateViewController(identifier: "SpeakDetail") as? ViewController)!
vc.textscannerized = TextScan.text
self.navigationController?.pushViewController(vc, animated: true)
}
}
You can try something like this:
scannerViewController.view.tintColor = .red

How can I record AVDepthData video and save in the gallery?

I am developing an application to record RGB-D sequences with the iPhone by using the DualRearCamera or the TrueDepthCamera. I can capture and visualize the RGB frame and depth frames and I developed a version where I can compress this data and save in the internal files of the iPhone. Nevertheless, my idea is to save both sequences (RGB and depth map sequences) in the gallery, but I am having problems to use AVAssetWritter and create a depth map video.
I am using the iPhone X, Xcode 10.2.1 and swift 5
import UIKit
import AVFoundation
import AssetsLibrary
var noMoreSpace = false
class ViewController: UIViewController{
#IBOutlet weak var previewView: UIImageView!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var previewModeControl: UISegmentedControl!
let session = AVCaptureSession()
let dataOutputQueue = DispatchQueue(label: "video data queue")
let videoOutput = AVCaptureVideoDataOutput()
let movieOutput = AVCaptureMovieFileOutput()
let depthOutput = AVCaptureDepthDataOutput()
let depthCapture = DepthCapture()
var previewLayer = AVCaptureVideoPreviewLayer()
var inputDevice: AVCaptureDeviceInput!
let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTrueDepthCamera], mediaType: .video, position: .unspecified)
var Timestamp: String {
let currentDate = NSDate()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "ddMM_HHmmss"
return "\(dateFormatter.string(from: currentDate as Date))"
}
var isRecording = false
var time = 0
var timer = Timer()
enum PreviewMode: Int {
case original
case depth
}
var previewMode = PreviewMode.original
var depthMap: CIImage?
var scale: CGFloat = 0.0
//let sessionQueue = DispatchQueue(label: "session queue")
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.isHidden = true //TODO: Disable the rest of the UI
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
configureCaptureSession()
session.startRunning()
}
func configureCaptureSession() {
session.beginConfiguration()
let camera = AVCaptureDevice.default(.builtInTrueDepthCamera, for: .video, position: .unspecified)!
do {
let cameraInput = try AVCaptureDeviceInput(device: camera)
if session.canAddInput(cameraInput){
session.sessionPreset = .vga640x480
session.addInput(cameraInput)
self.inputDevice = cameraInput
}
if session.canAddOutput(videoOutput){
videoOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
session.addOutput(videoOutput)
let videoConnection = videoOutput.connection(with: .video)
videoConnection?.videoOrientation = .portrait
//previewLayer = AVCaptureVideoPreviewLayer(session: session)
//previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
//previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
//previewView.layer.addSublayer(previewLayer)
//previewLayer.position = CGPoint(x: self.previewView.frame.width / 2, y: self.previewView.frame.height / 2)
//previewLayer.bounds = previewView.frame
}
//Add Depth output to the session
if session.canAddOutput(depthOutput){
session.addOutput(depthOutput)
depthOutput.setDelegate(self, callbackQueue: dataOutputQueue)
depthOutput.isFilteringEnabled = true
let depthConnection = depthOutput.connection(with: .depthData)
depthConnection?.videoOrientation = .portrait
}
/*if session.canAddOutput(movieOutput){
session.addOutput(movieOutput)
}*/
} catch {
print("Error")
}
let outputRect = CGRect(x: 0, y: 0, width: 1, height: 1)
let videoRect = videoOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
let depthRect = depthOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
// Calculate the scaling factor between videoRect and depthRect
scale = max(videoRect.width, videoRect.height) / max(depthRect.width, depthRect.height)
// Change the AVCaptureDevice configuration, so you need to lock it
do{
try camera.lockForConfiguration()
// Set the AVCaptureDevice‘s minimum frame duration (which is the inverse of the maximum frame rate) to be equal to the supported frame rate of the depth data
if let frameDuration = camera.activeDepthDataFormat?.videoSupportedFrameRateRanges.first?.minFrameDuration{
camera.activeVideoMinFrameDuration = frameDuration
}
// Unlock the configuration you locked
camera.unlockForConfiguration()
}catch{
fatalError(error.localizedDescription)
}
session.commitConfiguration()
}
#IBAction func startStopRecording(_ sender: Any) {
if isRecording{
stopRecording()
} else {
startRecording()
}
}
func startRecording(){
timeLabel.isHidden = false
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.timerAction), userInfo: nil, repeats: true)
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let flagTime = Timestamp
let auxStr = flagTime+"_output.mp4"
let fileUrl = paths[0].appendingPathComponent(auxStr)
depthCapture.prepareForRecording(timeFlag: flagTime)
movieOutput.startRecording(to: fileUrl, recordingDelegate: self)
print(fileUrl.absoluteString)
print("Recording started")
self.isRecording = true
}
func stopRecording(){
timeLabel.isHidden = true
timer.invalidate()
time = 0
timeLabel.text = "0"
movieOutput.stopRecording()
print("Stopped recording!")
self.isRecording = false
do {
try depthCapture.finishRecording(success: { (url: URL) -> Void in
print(url.absoluteString)
})
} catch {
print("Error while finishing depth capture.")
}
}
#objc func timerAction() {
time += 1
timeLabel.text = String(time)
}
#IBAction func previeModeChanged(_ sender: UISegmentedControl) {
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
}
#IBAction func switchCamera(_ sender: Any) {
let currentDevice = self.inputDevice.device
let currentPosition = currentDevice.position
let preferredPosition: AVCaptureDevice.Position
let preferredDeviceType: AVCaptureDevice.DeviceType
let devices = self.videoDeviceDiscoverySession.devices
var newVideoDevice: AVCaptureDevice? = nil
switch currentPosition {
case .unspecified, .front:
preferredPosition = .back
preferredDeviceType = .builtInDualCamera
case .back:
preferredPosition = .front
preferredDeviceType = .builtInTrueDepthCamera
#unknown default:
preferredPosition = .back
preferredDeviceType = .builtInDualCamera
}
// First, seek a device with both the preferred position and device type. Otherwise, seek a device with only the preferred position. ENTENDER MEJOR LQS CONDICIONES
if let device = devices.first(where: { $0.position == preferredPosition && $0.deviceType == preferredDeviceType }) {
newVideoDevice = device
} else if let device = devices.first(where: { $0.position == preferredPosition }) {
newVideoDevice = device
}
if let videoDevice = newVideoDevice {
do {
let cameraInput = try AVCaptureDeviceInput(device: videoDevice)
self.session.beginConfiguration()
self.session.removeInput(self.inputDevice)
if self.session.canAddInput(cameraInput) {
session.sessionPreset = .vga640x480
self.session.addInput(cameraInput)
self.inputDevice = cameraInput
}else {
self.session.addInput(self.inputDevice)
}
self.session.commitConfiguration()
} catch{
print("Error occurred while creating video device input: \(error)")
}
}
}
}
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate{
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let image = CIImage(cvPixelBuffer: pixelBuffer!)
let previewImage: CIImage
switch previewMode {
case .original:
previewImage = image
case .depth:
previewImage = depthMap ?? image
}
let displayImage = UIImage(ciImage: previewImage)
DispatchQueue.main.async {[weak self] in self?.previewView.image = displayImage}
}
}
extension ViewController: AVCaptureDepthDataOutputDelegate{
func depthDataOutput(_ output: AVCaptureDepthDataOutput, didOutput depthData: AVDepthData, timestamp: CMTime, connection: AVCaptureConnection) {
var convertedDepth: AVDepthData
// Ensure the depth data is the format you need: 32 bit FP disparity.???
if depthData.depthDataType != kCVPixelFormatType_DepthFloat16{
convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DepthFloat32)
}else{
convertedDepth = depthData
}
// You save the depth data map from the AVDepthData object as a CVPixelBuffer
let pixelBuffer = convertedDepth.depthDataMap
//Using an extension, you then clamp the pixels in the pixel buffer to keep them between 0.0 and 1.0.
pixelBuffer.clamp()
// Convert the pixel buffer into a CIImage
let depthMap = CIImage(cvPixelBuffer: pixelBuffer)
// You store depthMap in a class variable for later use
DispatchQueue.main.async {
[weak self] in self?.depthMap = depthMap
}
}
}

Error with Apple's SFSpeechRecognition when app in Background Mode (phone locked)

I am attempting to use Apple's SFSpeechRecognition (xcode 9, ios11, swift 4). The following code uses a timer to start a new request for speech recognition. I have background capability on, microphone usage granted and speech recognition granted. If the phone is unlocked, everything works as expected. But when the phone is locked, I receive the following error:
2018-02-20 22:24:47.522562-0500 Speech-Recognition-Demo[3505:1234188] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=1700 "(null)"
According to this link, speech recognition doesn't seem to work when app is in background mode but this information is old. I'm hoping someone has solved this or found a workaround.
Has anyone solved this problem or can anyone suggest something to try? My alternative is to require the Apple Watch for my app and I'd REALLY like to avoid that...
import UIKit
import Speech
class SpeechDetectionViewController: UIViewController,
SFSpeechRecognizerDelegate {
#IBOutlet weak var detectedTextLabel: UILabel!
#IBOutlet weak var colorView: UIView!
#IBOutlet weak var startButton: UIButton!
let audioEngine = AVAudioEngine()
let speechRecognizer: SFSpeechRecognizer? = SFSpeechRecognizer()
var request: SFSpeechAudioBufferRecognitionRequest?
var recognitionTask: SFSpeechRecognitionTask?
var isRecording = false
// timers
var timer = Timer()
let timerInterval = 5.0
var secondsElapsed = 0
// toggle for taking commands
var takeCommands = true
override func viewDidLoad() {
super.viewDidLoad()
self.requestSpeechAuthorization()
timer = Timer.scheduledTimer(timeInterval: timerInterval,target: self,selector: #selector(timerAction(timer:)) ,userInfo: nil,repeats: true)
}
#objc func timerAction(timer:Timer){
/* if takeCommands {
startRecording()
} else {
stopRecording()
}
takeCommands = !takeCommands
print("takeCommands: \(takeCommands)")
*/
startRecording()
}
//MARK: IBActions and Cancel
#IBAction func startButtonTapped(_ sender: UIButton) {
startRecording()
}
func startRecording(){
if isRecording {
print("STOP talking.")
request?.endAudio() // Added line to mark end of recording
request = nil
//audioEngine.stop()
if let node = audioEngine.inputNode {
node.removeTap(onBus: 0)
}
recognitionTask?.cancel()
isRecording = false
startButton.backgroundColor = UIColor.gray
} else {
print("START talking.")
self.recordAndRecognizeSpeech()
isRecording = true
startButton.backgroundColor = UIColor.red
}
}
func stopRecording() {
//audioEngine.stop()
if let node = audioEngine.inputNode {
node.removeTap(onBus: 0)
}
recognitionTask?.cancel()
isRecording = false
startButton.backgroundColor = UIColor.gray
}
//MARK: - Recognize Speech
func recordAndRecognizeSpeech() {
request = SFSpeechAudioBufferRecognitionRequest()
guard let node = audioEngine.inputNode else { return }
let recordingFormat = node.outputFormat(forBus: 0)
node.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
self.request?.append(buffer)
}
if !audioEngine.isRunning {
audioEngine.prepare()
do {
try audioEngine.start()
} catch {
self.sendAlert(message: "There has been an audio engine error.")
return print(error)
}
}
guard let myRecognizer = SFSpeechRecognizer() else {
self.sendAlert(message: "Speech recognition is not supported for your current locale.")
return
}
if !myRecognizer.isAvailable {
self.sendAlert(message: "Speech recognition is not currently available. Check back at a later time.")
// Recognizer is not available right now
return
}
recognitionTask = speechRecognizer?.recognitionTask(with: request!, resultHandler: { result, error in
if result != nil { // check to see if result is empty (i.e. no speech found)
if let result = result {
let bestString = result.bestTranscription.formattedString
self.detectedTextLabel.text = bestString
var lastString: String = ""
for segment in result.bestTranscription.segments {
let indexTo = bestString.index(bestString.startIndex, offsetBy: segment.substringRange.location)
lastString = bestString.substring(from: indexTo)
}
self.checkForColorsSaid(resultString: lastString)
} else if let error = error {
self.sendAlert(message: "There has been a speech recognition error")
print(error)
}
}
})
}
//MARK: - Check Authorization Status
func requestSpeechAuthorization() {
SFSpeechRecognizer.requestAuthorization { authStatus in
OperationQueue.main.addOperation {
switch authStatus {
case .authorized:
self.startButton.isEnabled = true
case .denied:
self.startButton.isEnabled = false
self.detectedTextLabel.text = "User denied access to speech recognition"
case .restricted:
self.startButton.isEnabled = false
self.detectedTextLabel.text = "Speech recognition restricted on this device"
case .notDetermined:
self.startButton.isEnabled = false
self.detectedTextLabel.text = "Speech recognition not yet authorized"
}
}
}
}
//MARK: - UI / Set view color.
func checkForColorsSaid(resultString: String) {
switch resultString {
case "red":
colorView.backgroundColor = UIColor.red
case "orange":
colorView.backgroundColor = UIColor.orange
case "yellow":
colorView.backgroundColor = UIColor.yellow
case "green":
colorView.backgroundColor = UIColor.green
case "blue":
colorView.backgroundColor = UIColor.blue
case "purple":
colorView.backgroundColor = UIColor.purple
case "black":
colorView.backgroundColor = UIColor.black
case "white":
colorView.backgroundColor = UIColor.white
case "gray":
colorView.backgroundColor = UIColor.gray
default: break
}
}
//MARK: - Alert
func sendAlert(message: String) {
let alert = UIAlertController(title: "Speech Recognizer Error", message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}

App Only Crashes for Iphone 6 and IPad in IOS 11 using Vision & Machine Learning API

I made a live translation app that identifies an object and translates it using the user's camera. It works just fine on my iPhone 6s and doesn't crash in any of the simulators, but when I run it on an iPhone 6, it crashes as soon I try to segue to the camera feed. Apple also says it crashes on the iPad as well.
Do certain devices just not support Vision API or is something wrong with my code?
import UIKit
import AVKit
import Vision
var lang = ""
var lang2 = ""
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, AVCapturePhotoCaptureDelegate {
#IBAction func screenshotB(_ sender: Any) {
//screenshot camera screen view
}
#IBOutlet weak var screenshotBOutlet: UIButton!
#IBOutlet weak var swirlyGuy: UIActivityIndicatorView!
#IBOutlet weak var title1: UILabel!
#IBOutlet weak var settingsButtonOutlet: UIButton!
#IBOutlet weak var launchScreen: UIViewX!
#IBOutlet weak var launchScreenLogo: UIImageView!
func stopSwirlyGuy(){
swirlyGuy.stopAnimating()
}
let identifierLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor(red: 0, green: 0, blue:0, alpha: 0.4)
label.textColor = .white
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
#IBAction func prepareForUnwind (segue:UIStoryboardSegue) {
}
override func viewDidLoad() {
super.viewDidLoad()
launchScreen.alpha = 1
launchScreenLogo.alpha = 1
swirlyGuy.startAnimating()
// start up the camera
let captureSession = AVCaptureSession()
captureSession.sessionPreset = .hd4K3840x2160
guard let captureDevice = AVCaptureDevice.default(for: .video) else { return }
guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
captureSession.addInput(input)
captureSession.startRunning()
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
view.layer.addSublayer(previewLayer)
previewLayer.frame = view.frame
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
captureSession.addOutput(dataOutput)
setupIdentifierConfidenceLabel()
setupSettingsButton()
setupTitle()
setupSwirlyGuy()
setupScreenshot()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1.5) {
self.launchScreen.alpha = 0
self.launchScreenLogo.alpha = 0
}
}
fileprivate func setupSettingsButton() {
view.addSubview(settingsButtonOutlet)
}
fileprivate func setupScreenshot() {
view.addSubview(screenshotBOutlet)
}
fileprivate func setupSwirlyGuy() {
view.addSubview(swirlyGuy)
}
fileprivate func setupTitle() {
view.addSubview(title1)
}
fileprivate func setupIdentifierConfidenceLabel() {
view.addSubview(identifierLabel)
identifierLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
identifierLabel.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
identifierLabel.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
identifierLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
identifierLabel.numberOfLines = 0
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// print("Camera was able to capture a frame:", Date())
guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
// model
guard let model = try? VNCoreMLModel(for: Resnet50().model) else { return }
let request = VNCoreMLRequest(model: model) { (finishedReq, err) in
//perhaps check the err
// print(finishedReq.results)
guard let results = finishedReq.results as? [VNClassificationObservation] else { return }
guard let firstObservation = results.first else { return }
print(firstObservation.identifier, firstObservation.confidence)
let x = (firstObservation.confidence)
let y = (x * 10000).rounded() / 10000
let z = (firstObservation.identifier)
let s = (self.translateSpanish(object1: firstObservation.identifier))
let f = (self.translateFrench(object1: firstObservation.identifier))
// var lang = ""
// var lang2 = ""
if language == "English" {
lang = z
}
else if language == "Spanish" {
lang = s
}
else {
lang = f
}
if language2 == "Spanish" {
lang2 = s
}
else if language2 == "English" {
lang2 = z
}
else {
lang2 = f
}
DispatchQueue.main.async {
self.identifierLabel.text = "\(lang)" + " = " + "\(lang2) \n \(y * 100)% accuracy"
self.stopSwirlyGuy()
}
}
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
}
//Translation fucntions omitted for brevity
This is the code for the view controller that segues into the main screen where the camera feed and Vision processing take place.
import UIKit
class FirstLaunchViewController: UIViewController {
#IBOutlet weak var title1: UILabelX!
#IBOutlet weak var logo1: UIImageView!
#IBOutlet weak var description1: UILabel!
#IBOutlet weak var buttonOutlet: UIButtonX!
#IBOutlet weak var initialBackground: UIViewX!
#IBOutlet weak var initialLogo: UIImageView!
#IBAction func toVC(_ sender: Any) {
UserDefaults.standard.set(false, forKey: "name")
performSegue(withIdentifier: "toMain", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
initialLogo.alpha = 1
initialBackground.alpha = 1
title1.alpha = 0
logo1.alpha = 0
description1.alpha = 0
buttonOutlet.alpha = 0
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1.5, animations: {
self.initialLogo.alpha = 0
self.initialBackground.alpha = 0
}) { (true) in
self.initialBackgroundGone()
}
}
func initialBackgroundGone() {
UIView.animate(withDuration: 1.5, animations: {
self.title1.alpha = 1
}) { (true) in
self.showBackgroundAgain()
}
}
func showBackgroundAgain() {
UIView.animate(withDuration: 1.3, animations: {
self.logo1.alpha = 1
}) { (true) in
self.showTitle()
}
}
func showTitle() {
UIView.animate(withDuration: 1.5, animations: {
self.description1.alpha = 1
}) { (true) in
self.showEverythingElse()
}
}
func showEverythingElse() {
UIView.animate(withDuration: 3.5) {
self.buttonOutlet.alpha = 1
}
}
}
This is a lot of code but I think your issue comes from the video preset your are using as iPhone 6 doesn't have support for 4K video recording.
When setting the session preset you should test that it is supported by all the targeted devices:
if captureSession.canSetSessionPreset(.hd4K3840x2160) {
captureSession.sessionPreset = .hd4K3840x2160
} else {
captureSession.sessionPreset = .high // or any other preset that suits your needs
}

Swift 3: How do I enable flash on custom AVFoundation camera?

I have a very basic AVFoundation Camera that has a captureButton that will take a photo and send that photo to the secondCameraController for it to be displayed. My problem is that there is a lot of iOS 10 deprecation and I'm not sure how I add in a flash when I press the captureButton. Any help will be highly appreciated. My code is below. Thank you guys.
class CameraController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
let captureSession = AVCaptureSession()
var previewLayer: CALayer!
var captureDevice: AVCaptureDevice!
var takePhoto: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareCamera()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: true)
}
let cameraView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
func prepareCamera() {
captureSession.sessionPreset = AVCaptureSessionPresetPhoto
if let availableDevices = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back).devices {
captureDevice = availableDevices.first
beginSession()
}
}
func beginSession() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput)
} catch {
print(error.localizedDescription)
}
if let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) {
self.previewLayer = previewLayer
self.view.layer.addSublayer(self.previewLayer)
self.previewLayer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.addSubview(captureButton)
let width: CGFloat = 85
captureButton.frame = CGRect(x: (previewLayer.frame.width / 2) - width / 2, y: (previewLayer.frame.height) - width - 25, width: width, height: 85)
captureSession.startRunning()
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString): NSNumber(value: kCVPixelFormatType_32BGRA)]
dataOutput.alwaysDiscardsLateVideoFrames = true
if captureSession.canAddOutput(dataOutput) {
captureSession.addOutput(dataOutput)
}
captureSession.commitConfiguration()
let queue = DispatchQueue(label: "com.cheekylabsltd.camera")
dataOutput.setSampleBufferDelegate(self, queue: queue)
}
}
func handleCapture() {
takePhoto = true
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
if takePhoto {
takePhoto = false
if let image = self.getImageFromSampleBuffer(buffer: sampleBuffer) {
let secondController = SecondCameraController()
secondController.takenPhoto = image
DispatchQueue.main.async {
self.present(secondController, animated: true, completion: {
self.stopCaptureSession()
})
}
}
}
}
func getImageFromSampleBuffer(buffer: CMSampleBuffer) -> UIImage? {
if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let context = CIContext()
let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
if let image = context.createCGImage(ciImage, from: imageRect) {
return UIImage(cgImage: image, scale: UIScreen.main.scale, orientation: .right)
}
}
return nil
}
func stopCaptureSession() {
self.captureSession.stopRunning()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
self.captureSession.removeInput(input)
}
}
}
lazy var captureButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .white
button.layer.cornerRadius = 42.5
button.clipsToBounds = true
button.alpha = 0.40
button.layer.borderWidth = 4
button.layer.borderColor = greenColor.cgColor
button.addTarget(self, action: #selector(handleCapture), for: .touchUpInside)
return button
}()
}
Try this code :
Swift v3.0
private func flashOn(device:AVCaptureDevice)
{
do{
if (device.hasTorch)
{
try device.lockForConfiguration()
device.torchMode = .on
device.flashMode = .on
device.unlockForConfiguration()
}
}catch{
//DISABEL FLASH BUTTON HERE IF ERROR
print("Device tourch Flash Error ");
}
}
//FOR FLASH OFF CODE
private func flashOff(device:AVCaptureDevice)
{
do{
if (device.hasTorch){
try device.lockForConfiguration()
device.torchMode = .off
device.flashMode = .off
device.unlockForConfiguration()
}
}catch{
//DISABEL FLASH BUTTON HERE IF ERROR
print("Device tourch Flash Error ");
}
}
// METHOD
//private let session = AVCaptureSession()
//MARK: FLASH UITLITY METHODS
func toggleFlash() {
var device : AVCaptureDevice!
if #available(iOS 10.0, *) {
let videoDeviceDiscoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDuoCamera], mediaType: AVMediaTypeVideo, position: .unspecified)!
let devices = videoDeviceDiscoverySession.devices!
device = devices.first!
} else {
// Fallback on earlier versions
device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
}
if ((device as AnyObject).hasMediaType(AVMediaTypeVideo))
{
if (device.hasTorch)
{
self.session.beginConfiguration()
//self.objOverlayView.disableCenterCameraBtn();
if device.isTorchActive == false {
self.flashOn(device: device)
} else {
self.flashOff(device: device);
}
//self.objOverlayView.enableCenterCameraBtn();
self.session.commitConfiguration()
}
}
}
Swift 4
So there are two different behaviors to choose from in AVFoundation. One would be a capture device torch switch. Connect the torchSwitch action to some view and be sure to change CameraManager.shared.backDevice to your instance of the front or back device that provides the current input.
#IBAction func torchSwitch(_ sender: Any) {
guard let device = CameraManager.shared.backDevice else { return }
guard device.isTorchAvailable else { return }
do {
try device.lockForConfiguration()
device.torchMode = device.torchMode ? .off : .on
if device.torchMode == .on {
try device.setTorchModeOn(level: 0.7)
}
} catch {
debugPrint(error)
}
}
AVFoundation has deprecated device.flashMode
Now to set flash, declare a variable on camera or vc. The value here will be the default.
var flash: AVCaptureFlashMode = .off
Connect this action to some view
#IBAction func torchSwitch(_ sender: Any) { flash = flash ? .off : .on }
Then when you want to capture an image, use AVCapturePhotoOutput and prepare the photo settings. stillCameraOutput is an instance of AVCapturePhotoOutput.
let settings = AVCapturePhotoSettings()
settings.flashMode = flash
stillCameraOutput.capturePhoto(with: settings, delegate: self)
Swift 4 :
Following code is working fine for me
private enum FlashPhotoMode {
case on
case off
}
#IBOutlet weak var flashPhotoModeButton: UIButton!
#IBAction func toggleFlashPhotoMode(_ flashPhotoModeButton: UIButton ) {
sessionQueue.async {
self.flashPhotoMode = (self.flashPhotoMode == .on) ? .off : .on
let flashPhotoMode = self.flashPhotoMode
DispatchQueue.main.async {
if flashPhotoMode == .on {
self.flashPhotoModeButton.setBackgroundImage(UIImage(named: "flashON"), for: .normal)
print("flashON")
} else {
self.flashPhotoModeButton.setBackgroundImage(UIImage(named: "flashOFF"), for: .normal)
print("flashOFF")
}
}
}
}
#IBAction private func capturePhoto(_ photoButton: UIButton) {
................
.......................
if self.videoDeviceInput.device.isFlashAvailable {
if self.flashPhotoMode == .on {
photoSettings.flashMode = .on
print("FLASH ON ")
} else {
print("FLASH OFF ")
photoSettings.flashMode = .off
}
}
}
Thanks!