iOS internal microphone release issue with Openvidu - ios

I am using Openvidu for call in my iOS app and it is working fine. But When I am closing call, app is not releasing microphone, an orange dot always showing on the status bar. To closing call I am using same code which Openvidu team provided but not worked.
This code provided by Openvidu team
override func dispose() {
super.dispose()
if (videoTrack != nil){
self.videoTrack?.remove(self.renderer!)
}
self.videoTrack = nil
}
In the super.dispose() method they are closing peer connection that is
func dispose() { // This method called from previous method as super.dispose()
peerConnection?.close()
peerConnection = nil
}
With this code both camera and microphone was not releasing so I added code to stop capture in the dispose method and it started releasing camera but microphone issue is present.
override func dispose() {
super.dispose()
if let videoCapturer = self.videoCapturer as? RTCCameraVideoCapturer {
videoCapturer.stopCapture()
}
if (videoTrack != nil){
self.videoTrack?.remove(self.renderer!)
}
self.videoTrack = nil
}
To resolve microphone issue I tried several code and one of them worked. But the problem is after releasing microphone next call is not getting microphone to use and no audio there.The code I used is
override func dispose() {
super.dispose()
if let videoCapturer = self.videoCapturer as? RTCCameraVideoCapturer {
videoCapturer.stopCapture()
}
self.videoCapturer = nil
if (videoTrack != nil && self.renderer != nil){
self.videoTrack?.remove(self.renderer!)
}
self.renderer = nil
self.videoTrack = nil
self.audioTrack = nil
// This code is releasing microphone but next time not able to use microphone
self.rtcAudioSession.lockForConfiguration()
do {
try self.rtcAudioSession.setActive(false)
} catch let error {
debugPrint("Error deactivating AVAudioSession: \(error)")
}
self.rtcAudioSession.unlockForConfiguration()
}
any help would be appreciated

Related

AudioKit microphone doesn't work if permission was not granted on app launch

I am facing a strange issue with AudioKit 5.2.2. If the app is launched without microphone permission granted, then the microphone wouldn't work even if the user granted permission while the app is running. It requires reopening the app after granting permission to make it work properly.
The microphone doesn't throw any errors but it doesn't seem to receive any data if the permission was not granted.
I must also say that the permission is requested from the Flutter code, but if I check the permission status through native code it also returns granted status.
Also, the setup function is always called after the permission is granted.
Here is min reproducible code:
var engine: AudioEngine!
var silencer: Fader!
var mixer: Mixer!
var tracker: AmplitudeTap!
func setup() -> Bool {
if engine == nil {
engine = AudioEngine()
}
if mixer == nil {
mixer = Mixer()
}
guard let input = engine.input else {
return false
}
if silencer == nil {
let silencer = Fader(input, gain: 0)
self.silencer = silencer
}
engine.output = mixer
tracker = AmplitudeTap(input) { amplitude in
DispatchQueue.main.async {
print("amplitude: \(amplitude)") // <- nothing is received here is the permission is not granted initially
}
}
return true
}
func start() {
do {
try tracker.start()
try engine.start()
} catch let err {
print(err)
}
}
func stop() {
engine.stop()
tracker.stop()
mixer.removeAllInputs()
engine.output = nil
tracker.dispose()
}

CallKit Audio session starts when navigating to the application only

I'm working on a voip app now and want to support holding.
But when a second call comes and I hold my current call. Switching to my first call I hear no sound at all.
The way to hear it is to navigate from callKit native screen to my app and hence I can hear the voice.
func configureAudioSession() {
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, mode: .videoChat, options: AVAudioSession.CategoryOptions.mixWithOthers)
_ = try? AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.none)
_ = try? AVAudioSession.sharedInstance().setMode(AVAudioSession.Mode.voiceChat)
}
func startAudio() {
print("Starting audio")
do {
_ = try AVAudioSession.sharedInstance().setActive(true)
} catch {
}
}
func stopAudio() {
print("Stopping audio")
do {
_ = try AVAudioSession.sharedInstance().setActive(false)
} catch {
}
}
For supporting holding, you dont have to start/stop audio session, instead you can use CXSetHeldCallAction provided by Callkit itself. Here, is the code for hold that i use.
let callKitCallController = CXCallController()
func performHoldAction(isOnHold:Bool, uuid:UUID) {
let holdCallAction = CXSetHeldCallAction(call: uuid, onHold: isOnHold)
let transaction = CXTransaction(action: holdCallAction)
callKitCallController.request(transaction) { error in
if let error = error {
CPrint("holdCallAction transaction request failed: \(error.localizedDescription).")
return
}
CPrint("holdCallAction transaction request successful")
}
}
Once system puts the call on hold(by above method OR due to other incoming call accepting or any other reason), then in CXProviderDelegate, the method func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) gives you the callback for the detail.
Here, system/callkit itself interacts with audio, you dont have to explicitly start or stop audio for holding.
Note: Make sure that you given supportsHolding to true for the CXCallUpdate that you gave for new call.

Keep torch on while taking video iOS swift

I built a camera app for auto capture. I want to keep the flash on as long as the camera is on. I set the following code :
cameraDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
if (cameraDevice.hasTorch) {
do {
try cameraDevice.lockForConfiguration()
if cameraDevice.isTorchActive {
cameraDevice.torchMode = AVCaptureTorchMode.on
} else {
// sets the torch intensity to 100%
try cameraDevice.setTorchModeOnWithLevel(0.8)
}
cameraDevice.unlockForConfiguration()
} catch {
print(error)
}
}
But when I run the app, it only flashes for one time and then goes off. How can I solve this problem?
Call this method
Inside your camera active/Open func or When device camera active -
func flashActive() {
if let currentDevice = AVCaptureDevice.default(for: AVMediaType.video), currentDevice.hasTorch {
do {
try currentDevice.lockForConfiguration()
let torchOn = !currentDevice.isTorchActive
try currentDevice.setTorchModeOn(level:1.0)//Or whatever you want
currentDevice.torchMode = torchOn ? .on : .off
currentDevice.unlockForConfiguration()
} catch {
print("error")
}
}
}

Dispatching delegate callback to main queue, from another queue, AVFoundation

I am working on a camera that is wrapped up in a base viewController, with a delegate style interface for callbacks, so all I have to do as a client is subclass the camera view controller, implement the delegate methods, and add the UI buttons.
My question is about recording video. Video recording is started on a unique background task to ensure that the recording can be written to a temporary file. This is done on my
private let sessionQueue = DispatchQueue(label: "com.andrewferrarone.sessionQueue") session queue:
public func startRecording()
{
guard let movieFileOutput = self.movieFileOutput else { return }
//get video preview layer's video orientation on main queue
guard let videoPreviewLayerOrientation = self.previewView.videoPreviewLayer?.connection.videoOrientation else {
print("handleRecord: videoPreviewLayer is nil")
return
}
self.sessionQueue.async {
if !movieFileOutput.isRecording {
if UIDevice.current.isMultitaskingSupported {
self.backgroundRecordingID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
}
//update orientation on the movie file output video connection before recording
let movieFileOutputConnection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo)
movieFileOutputConnection?.videoOrientation = videoPreviewLayerOrientation
//start recording to a temporary file:
let outputFileName = UUID().uuidString
let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath: outputFilePath), recordingDelegate: self)
}
}
}
so the recording is setup as a background task dispatched to self.sessionQueue. When I stop recording I receive an AVCaptureFileOutputRecordingDelegate method. In this method, I want to callback my delegate with the filepath, and then cleanup. How do I ensure that the delegate can persist the recording from the temporary file path before cleanup happens and the temporary file is removed?
public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!)
{
//cleanup func for later
func cleanup()
{
let path = outputFileURL.path
if FileManager.default.fileExists(atPath: path) {
do {
try FileManager.default.removeItem(atPath: path)
}
catch let error {
print("Could not remove file at url: \(outputFileURL), error: \(error.localizedDescription)")
}
}
if let currentBackgroundRecordingID = self.backgroundRecordingID {
self.backgroundRecordingID = UIBackgroundTaskInvalid
if currentBackgroundRecordingID != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID)
}
}
}
var success = true
if error != nil {
print("Movie file finishing error: \(error)")
success = ((error as NSError).userInfo[AVErrorRecordingSuccessfullyFinishedKey] as AnyObject).boolValue
}
DispatchQueue.main.async {
self.delegate?.camera(self, didFinishRecordingToOutputFileAt: outputFileURL, success: success)
}
cleanup()
}
So I called my delegate back on the main queue with the results, but then I need to call cleanup() should I do this on the main queue right after calling back my delegate? is this safe? or if I leave it the way it is now, then we are on self.sessionQueue, and I am unsure if cleanup() will happen before the delegate method implementation has time to persist the recording. If anyone can give me some insight into what is going on and what would be the safest thing to do here, that would be great. According to apple, the documentation says do not assume that AVCaptureFileOutputRecordingDelegate didFinishRecordingToOutputFileAt method is called on a specific thread. Thanks for your time and help!
How about:
DispatchQueue.main.async {
self.delegate?.camera(self, didFinishRecordingToOutputFileAt: outputFileURL, success: success)
self.sessionQueue.async {
cleanup()
}
}
I think that would be the standard way of handling this situation. When the delegate method finishes, you assume that the delegate is done with the file (or copied it somewhere else).

Can a AVCaptureFileOutputRecordingDelegate be added to a subclass UIView?

I am having an issue creating a capture session in a custom UIView. I set the delegate like this
class Camera: UIView, AVCaptureFileOutputRecordingDelegate, AVAudioRecorderDelegate {
}
and then I set everything up and set the delegate like this
self.recordingDelegate? = self
captureSession.sessionPreset = AVCaptureSessionPresetHigh
let devices = AVCaptureDevice.devices()
for device in devices {
if (device.hasMediaType(AVMediaTypeVideo)) {
if(device.position == AVCaptureDevicePosition.Back) {
captureDevice = device as? AVCaptureDevice
if captureDevice != nil {
beginSession()
}
}
}
}
and all goes well. However, in the beginSession function:
func beginSession() {
let err : NSError? = nil
do {
self.captureSession.addInput(try AVCaptureDeviceInput(device: self.captureDevice!))
}
catch {
print("dang")
}
if err != nil {
print("error: \(err?.localizedDescription)")
}
...
The catch is thrown when I try to add the capture device input and there for it is not being added and I can not figure out why.
All of my code I am currently using was working fine before when I had it inside a UIViewController but when I switched it over to a subclass of UIView it stopped working. Any help would be appreciated if more code is needed let me know thank you!
I figured it out the iOS device I was using did not have the camera enabled for some reason there for the input could not be added which made the preview layer unable to capture any data

Resources