diff --git a/dist/cordova-plugin-iosrtc.js b/dist/cordova-plugin-iosrtc.js index c1efd16f..4b467334 100644 --- a/dist/cordova-plugin-iosrtc.js +++ b/dist/cordova-plugin-iosrtc.js @@ -2,8 +2,8 @@ * cordova-plugin-iosrtc v5.0.0 * Cordova iOS plugin exposing the full WebRTC W3C JavaScript APIs * Copyright 2015-2017 eFace2Face, Inc. (https://eface2face.com) - * Copyright 2017-2019 BasqueVoIPMafia (https://github.com/BasqueVoIPMafia) - * Copyright 2019 Cordova-RTC (https://github.com/cordova-rtc) + * Copyright 2015-2019 BasqueVoIPMafia (https://github.com/BasqueVoIPMafia) + * Copyright 2017-2019 Cordova-RTC (https://github.com/cordova-rtc) * License MIT */ @@ -2357,6 +2357,11 @@ function getUserMedia(constraints) { // Also check sourceId (mangled by adapter.js). } else if (typeof constraints.video.sourceId === 'string') { newConstraints.videoDeviceId = constraints.video.sourceId; + } else if (typeof constraints.video.deviceId === 'object') { + newConstraints.videoDeviceId = !!constraints.video.deviceId.exact ? constraints.video.deviceId.exact : constraints.video.deviceId.ideal; + if (Array.isArray(newConstraints.videoDeviceId)) { + newConstraints.videoDeviceId = newConstraints.videoDeviceId[0]; + } } // Get requested min/max width. @@ -2391,6 +2396,22 @@ function getUserMedia(constraints) { } } + // Get audio constraints + if (audioRequested) { + // Get requested audio deviceId. + if (typeof constraints.audio.deviceId === 'string') { + newConstraints.audioDeviceId = constraints.audio.deviceId; + // Also check sourceId (mangled by adapter.js). + } else if (typeof constraints.audio.sourceId === 'string') { + newConstraints.audioDeviceId = constraints.audio.sourceId; + } else if (typeof constraints.audio.deviceId === 'object') { + newConstraints.audioDeviceId = !!constraints.audio.deviceId.exact ? constraints.audio.deviceId.exact : constraints.audio.deviceId.ideal; + if (Array.isArray(newConstraints.audioDeviceId)) { + newConstraints.audioDeviceId = newConstraints.audioDeviceId[0]; + } + } + } + debug('[computed constraints:%o]', newConstraints); if (isPromise) { @@ -2433,7 +2454,6 @@ function getUserMedia(constraints) { exec(onResultOK, onResultError, 'iosrtcPlugin', 'getUserMedia', [newConstraints]); } - },{"./Errors":1,"./MediaStream":3,"cordova/exec":undefined,"debug":17}],15:[function(_dereq_,module,exports){ (function (global){ /** diff --git a/js/getUserMedia.js b/js/getUserMedia.js index 22adccca..01a7b7d9 100644 --- a/js/getUserMedia.js +++ b/js/getUserMedia.js @@ -97,6 +97,11 @@ function getUserMedia(constraints) { // Also check sourceId (mangled by adapter.js). } else if (typeof constraints.video.sourceId === 'string') { newConstraints.videoDeviceId = constraints.video.sourceId; + } else if (typeof constraints.video.deviceId === 'object') { + newConstraints.videoDeviceId = !!constraints.video.deviceId.exact ? constraints.video.deviceId.exact : constraints.video.deviceId.ideal; + if (Array.isArray(newConstraints.videoDeviceId)) { + newConstraints.videoDeviceId = newConstraints.videoDeviceId[0]; + } } // Get requested min/max width. @@ -131,6 +136,22 @@ function getUserMedia(constraints) { } } + // Get audio constraints + if (audioRequested) { + // Get requested audio deviceId. + if (typeof constraints.audio.deviceId === 'string') { + newConstraints.audioDeviceId = constraints.audio.deviceId; + // Also check sourceId (mangled by adapter.js). + } else if (typeof constraints.audio.sourceId === 'string') { + newConstraints.audioDeviceId = constraints.audio.sourceId; + } else if (typeof constraints.audio.deviceId === 'object') { + newConstraints.audioDeviceId = !!constraints.audio.deviceId.exact ? constraints.audio.deviceId.exact : constraints.audio.deviceId.ideal; + if (Array.isArray(newConstraints.audioDeviceId)) { + newConstraints.audioDeviceId = newConstraints.audioDeviceId[0]; + } + } + } + debug('[computed constraints:%o]', newConstraints); if (isPromise) { @@ -172,4 +193,4 @@ function getUserMedia(constraints) { } exec(onResultOK, onResultError, 'iosrtcPlugin', 'getUserMedia', [newConstraints]); -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 490bb313..6b21deb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2301,7 +2301,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2325,13 +2326,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2348,19 +2351,22 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2491,7 +2497,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2505,6 +2512,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2521,6 +2529,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2529,13 +2538,15 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2556,6 +2567,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2644,7 +2656,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2658,6 +2671,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2753,7 +2767,8 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2795,6 +2810,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2816,6 +2832,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2864,13 +2881,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/PluginEnumerateDevices.swift b/src/PluginEnumerateDevices.swift index cbe0a34f..e7883f39 100644 --- a/src/PluginEnumerateDevices.swift +++ b/src/PluginEnumerateDevices.swift @@ -6,52 +6,112 @@ import AVFoundation * Doc: https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVCaptureDevice_Class/index.html */ +struct MediaDeviceInfo { + let deviceId, groupId, kind, label: String + init(deviceId: String, kind: String, label: String) { + self.deviceId = deviceId + self.groupId = "" + self.kind = kind + self.label = label + } +} + +var audioInputSelected: AVAudioSessionPortDescription? = nil class PluginEnumerateDevices { class func call(_ callback: (_ data: NSDictionary) -> Void) { NSLog("PluginEnumerateDevices#call()") - - let devices = AVCaptureDevice.devices() - + initAudioDevices() + var audioDevices: [MediaDeviceInfo] = getAllAudioDevices() + let devices: [AVCaptureDevice] = AVCaptureDevice.DiscoverySession.init( deviceTypes: [ AVCaptureDevice.DeviceType.builtInMicrophone, AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: nil, position: AVCaptureDevice.Position.unspecified).devices let json: NSMutableDictionary = [ - "devices": NSMutableDictionary() + "devices": NSMutableDictionary() ] for device: AVCaptureDevice in devices { - var facing: String let hasAudio = device.hasMediaType(AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.audio))) let hasVideo = device.hasMediaType(AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video))) - switch device.position { - case AVCaptureDevice.Position.unspecified: - facing = "unknown" - case AVCaptureDevice.Position.back: - facing = "back" - case AVCaptureDevice.Position.front: - facing = "front" - } - - NSLog("- device [uniqueID:'%@', localizedName:'%@', facing:%@, audio:%@, video:%@, connected:%@]", - String(device.uniqueID), String(device.localizedName), String(facing), + NSLog("- device [uniqueID:'%@', localizedName:'%@', audio:%@, video:%@, connected:%@]", + String(device.uniqueID), String(device.localizedName), String(hasAudio), String(hasVideo), String(device.isConnected)) if device.isConnected == false || (hasAudio == false && hasVideo == false) { continue } - - (json["devices"] as! NSMutableDictionary)[device.uniqueID] = [ - "deviceId": device.uniqueID, - "kind": hasAudio ? "audioinput" : "videoinput", - "label": device.localizedName, - "facing": facing - ] + + if hasAudio == false { + audioDevices.append(MediaDeviceInfo(deviceId: device.uniqueID, kind: "videoinput", label: device.localizedName)) + } } + + // Casting to NSMutableDictionary + for device: MediaDeviceInfo in audioDevices { + (json["devices"] as! NSMutableDictionary)[device.deviceId] = [ + "deviceId": device.deviceId, + "kind": device.kind, + "label": device.label, + "groupId": device.groupId + ] + } + + print("DEVICES => ", json) + callback(json as NSDictionary) + } + + // Setter function inserted by save specific audio device + class func saveAudioDevice(inputDeviceUID: String) -> Void { + let audioSession: AVAudioSession = AVAudioSession.sharedInstance() + let audioInput: AVAudioSessionPortDescription = audioSession.availableInputs!.filter({ (value:AVAudioSessionPortDescription) -> Bool in + return value.uid == inputDeviceUID + })[0] + + audioInputSelected = audioInput + } + + // Setter function inserted by set specific audio device + class func setPreferredInput() -> Void { + let audioSession: AVAudioSession = AVAudioSession.sharedInstance() - callback(json) - } + print("SETTING INPUT SELECTED: ", audioInputSelected!) + + do { + try audioSession.setPreferredInput(audioInputSelected) + } catch { + print("Error setting audio device.") + } + } +} + +// Getter function inserted by get all audio devices connected +fileprivate func getAllAudioDevices() -> [MediaDeviceInfo] { + let audioSession: AVAudioSession = AVAudioSession.sharedInstance() + var audioDevicesArr : [MediaDeviceInfo] = [] + let audioInputDevices: [AVAudioSessionPortDescription] = audioSession.availableInputs! + + for audioInput in audioInputDevices { + audioDevicesArr.append(MediaDeviceInfo(deviceId: audioInput.uid, kind: "audioinput", label: audioInput.portName)) + + // Initialize audioInputSelected. Default Built-In Microphone + if audioInput.portType == AVAudioSession.Port.builtInMic { + PluginEnumerateDevices.saveAudioDevice(inputDeviceUID: audioInput.uid) + } + } + return audioDevicesArr +} + +fileprivate func initAudioDevices() -> Void { + let audioSession: AVAudioSession = AVAudioSession.sharedInstance() + + do { + try audioSession.setCategory(AVAudioSession.Category.playAndRecord, options: .allowBluetooth) + try audioSession.setActive(true) + } catch { + print("Error messing with audio session: \(error)") + } } // Helper function inserted by Swift 4.2 migrator. fileprivate func convertFromAVMediaType(_ input: AVMediaType) -> String { return input.rawValue -} +} \ No newline at end of file diff --git a/src/PluginGetUserMedia.swift b/src/PluginGetUserMedia.swift index 0aef5b52..dc702d3e 100644 --- a/src/PluginGetUserMedia.swift +++ b/src/PluginGetUserMedia.swift @@ -5,12 +5,11 @@ import AVFoundation class PluginGetUserMedia { var rtcPeerConnectionFactory: RTCPeerConnectionFactory - init(rtcPeerConnectionFactory: RTCPeerConnectionFactory) { NSLog("PluginGetUserMedia#init()") self.rtcPeerConnectionFactory = rtcPeerConnectionFactory - } + } deinit { @@ -29,6 +28,7 @@ class PluginGetUserMedia { let audioRequested = constraints.object(forKey: "audio") as? Bool ?? false let videoRequested = constraints.object(forKey: "video") as? Bool ?? false let videoDeviceId = constraints.object(forKey: "videoDeviceId") as? String + let audioDeviceId = constraints.object(forKey: "audioDeviceId") as? String let videoMinWidth = constraints.object(forKey: "videoMinWidth") as? Int ?? 0 let videoMaxWidth = constraints.object(forKey: "videoMaxWidth") as? Int ?? 0 let videoMinHeight = constraints.object(forKey: "videoMinHeight") as? Int ?? 0 @@ -86,20 +86,14 @@ class PluginGetUserMedia { // No specific video device requested. if videoDeviceId == nil { NSLog("PluginGetUserMedia#call() | video requested (device not specified)") - - for device: AVCaptureDevice in (AVCaptureDevice.devices(for: AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video))) ) { - if device.position == AVCaptureDevice.Position.front { - videoDevice = device - break - } - } + videoDevice = AVCaptureDevice.DiscoverySession.init(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera, AVCaptureDevice.DeviceType.builtInDualCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.front).devices[0] } // Video device specified. else { NSLog("PluginGetUserMedia#call() | video requested (specified device id: '%@')", String(videoDeviceId!)) - - for device: AVCaptureDevice in (AVCaptureDevice.devices(for: AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video))) ) { + let videoDevices: [AVCaptureDevice] = AVCaptureDevice.DiscoverySession.init(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera, AVCaptureDevice.DeviceType.builtInDualCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified).devices + for device: AVCaptureDevice in videoDevices { if device.uniqueID == videoDeviceId { videoDevice = device break @@ -174,6 +168,11 @@ class PluginGetUserMedia { rtcAudioTrack = self.rtcPeerConnectionFactory.audioTrack(withID: UUID().uuidString) rtcMediaStream.addAudioTrack(rtcAudioTrack!) + + if (audioDeviceId != nil) { + + PluginEnumerateDevices.saveAudioDevice(inputDeviceUID: audioDeviceId!) + } } pluginMediaStream = PluginMediaStream(rtcMediaStream: rtcMediaStream) @@ -191,4 +190,4 @@ class PluginGetUserMedia { // Helper function inserted by Swift 4.2 migrator. fileprivate func convertFromAVMediaType(_ input: AVMediaType) -> String { return input.rawValue -} +} \ No newline at end of file diff --git a/src/PluginRTCPeerConnection.swift b/src/PluginRTCPeerConnection.swift index ecdf655d..c46ce7a8 100644 --- a/src/PluginRTCPeerConnection.swift +++ b/src/PluginRTCPeerConnection.swift @@ -19,6 +19,7 @@ class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionD var onSetDescriptionSuccessCallback: (() -> Void)! var onSetDescriptionFailureCallback: ((_ error: Error) -> Void)! var onGetStatsCallback: ((_ array: NSArray) -> Void)! + var isAudioInputSelected: Bool = false init( rtcPeerConnectionFactory: RTCPeerConnectionFactory, @@ -223,6 +224,11 @@ class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionD sdp: candidate )) + if !self.isAudioInputSelected { + PluginEnumerateDevices.setPreferredInput() + self.isAudioInputSelected = true + } + var data: NSDictionary if result == true {