Skip to content

Commit

Permalink
Merge pull request #9 from OpenTelecom/video-support
Browse files Browse the repository at this point in the history
Video support
  • Loading branch information
noahmehl authored Dec 3, 2020
2 parents c760a0f + ef39071 commit aa50c2e
Show file tree
Hide file tree
Showing 43 changed files with 3,588 additions and 557 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ Carthage/Build
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/
Pods/
2 changes: 2 additions & 0 deletions Example/WKWebViewRTC/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>WKWebViewRTC requires acess to mic.</string>
<key>NSCameraUsageDescription</key>
<string>WKWebViewRTC requires acess to cam.</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
Expand Down
2 changes: 1 addition & 1 deletion Example/WKWebViewRTC/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ViewController: UIViewController {
// Do any additional setup after loading the view, typically from a nib.
WKWebViewRTC(wkwebview: webView, contentController: webView.configuration.userContentController)

webView.load(URLRequest(url: URL(string: "https://sip-phone-test.reper.io/?name=Display%20Name&websocket=wss://domain.com:5065&[email protected]&password=password")!))
webView.load(URLRequest(url: URL(string: "https://cordova-rtc.github.io/cordova-plugin-iosrtc-sample/index.html")!))
}
}

11 changes: 3 additions & 8 deletions WKWebViewRTC.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'WKWebViewRTC'
s.version = '0.2.0'
s.version = '0.3.0'
s.summary = 'WebRTC library for WKWebView for Swift on iOS'

# This description is used to generate tags and improve search results.
Expand All @@ -22,22 +22,17 @@ Pod::Spec.new do |s|
DESC

s.homepage = 'https://github.com/OpenTelecom/WKWebViewRTC'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'OpenTelecom' => '[email protected]' }
s.source = { :git => 'https://github.com/OpenTelecom/WKWebViewRTC.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

s.swift_version = '4.2'
s.ios.deployment_target = '11.0'

s.source_files = 'WKWebViewRTC/Classes/**/*'
s.resources = 'WKWebViewRTC/Js/jsWKWebViewRTC.js'
# s.resource_bundles = {
# 'WKWebViewRTC' => ['WKWebViewRTC/Js/jsWKWebViewRTC.js']
# }

# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'AVFoundation'
s.dependency 'GoogleWebRTC', '1.1.29229'

s.xcconfig = { 'ENABLE_BITCODE' => 'NO', 'ONLY_ACTIVE_ARCH' => 'Yes' }
end
157 changes: 125 additions & 32 deletions WKWebViewRTC/Classes/WKWebViewRTC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class WKWebViewRTC : NSObject {
super.init()

// Make the web view transparent

pluginMediaStreams = [:]
pluginMediaStreamTracks = [:]
pluginMediaStreamRenderers = [:]
Expand Down Expand Up @@ -79,6 +80,8 @@ public class WKWebViewRTC : NSObject {
func setWebView(webview:WKWebView?)
{
self.webView = webview
self.webView!.isOpaque = false
self.webView!.backgroundColor = UIColor.clear

}

Expand All @@ -104,12 +107,14 @@ public class WKWebViewRTC : NSObject {
}
}

func onReset() {
@objc(onReset) func onReset() {
NSLog("WKWebViewRTC#onReset() | doing nothing")
cleanup();
}

func onAppTerminate() {
@objc(onAppTerminate) func onAppTerminate() {
NSLog("WKWebViewRTC#onAppTerminate() | doing nothing")
cleanup();
}

@objc(new_RTCPeerConnection:) func new_RTCPeerConnection(_ command: WkWebviewCommand) {
Expand Down Expand Up @@ -142,7 +147,9 @@ public class WKWebViewRTC : NSObject {
self.emit(command.callbackId, result: result)
},
eventListenerForAddStream: self.saveMediaStream,
eventListenerForRemoveStream: self.deleteMediaStream
eventListenerForRemoveStream: self.deleteMediaStream,
eventListenerForAddTrack: self.saveMediaStreamTrack,
eventListenerForRemoveTrack: self.deleteMediaStreamTrack
)

// Store the pluginRTCPeerConnection into the dictionary.
Expand Down Expand Up @@ -407,21 +414,14 @@ public class WKWebViewRTC : NSObject {
@objc(RTCPeerConnection_removeTrack:) func RTCPeerConnection_removeTrack(_ command: WkWebviewCommand) {
let pcId = command.argument(at: 0) as! Int
let trackId = command.argument(at: 1) as! String
let streamId = command.argument(at: 2) as! String
let pluginRTCPeerConnection = self.pluginRTCPeerConnections[pcId]
let pluginMediaStream = self.pluginMediaStreams[streamId]
let pluginMediaStreamTrack = self.pluginMediaStreamTracks[trackId]

if pluginRTCPeerConnection == nil {
NSLog("WKWebViewRTC#RTCPeerConnection_removeTrack() | ERROR: pluginRTCPeerConnection with pcId=%@ does not exist", String(pcId))
return;
}

if pluginMediaStream == nil {
NSLog("WKWebViewRTC#RTCPeerConnection_removeTrack() | ERROR: pluginMediaStream with id=%@ does not exist", String(streamId))
return;
}

if pluginMediaStreamTrack == nil {
NSLog("WKWebViewRTC#RTCPeerConnection_removeTrack() | ERROR: pluginMediaStreamTrack with id=\(trackId) does not exist")
return;
Expand All @@ -430,7 +430,7 @@ public class WKWebViewRTC : NSObject {
self.queue.async { [weak pluginRTCPeerConnection, weak pluginMediaStreamTrack] in
pluginRTCPeerConnection?.removeTrack(pluginMediaStreamTrack!)
// TODO remove only if not used by other stream
self.deleteMediaStreamTrack(trackId)
// self.deleteMediaStreamTrack(pluginMediaStreamTrack!)
}
}

Expand Down Expand Up @@ -720,7 +720,7 @@ public class WKWebViewRTC : NSObject {

if self.pluginMediaStreams[streamId] == nil {
let rtcMediaStream : RTCMediaStream = self.rtcPeerConnectionFactory.mediaStream(withStreamId: streamId)
let pluginMediaStream = iMediaStream(rtcMediaStream: rtcMediaStream)
let pluginMediaStream = iMediaStream(rtcMediaStream: rtcMediaStream, streamId: streamId)
pluginMediaStream.run()

self.saveMediaStream(pluginMediaStream)
Expand Down Expand Up @@ -824,6 +824,43 @@ public class WKWebViewRTC : NSObject {
self.pluginMediaStreams[id] = nil
}

@objc(MediaStreamTrack_clone:) func MediaStreamTrack_clone(_ command: WkWebviewCommand) {
NSLog("WKWebViewRTC#MediaStreamTrack_clone()")

let existingTrackId = command.argument(at: 0) as! String
let newTrackId = command.argument(at: 1) as! String
let pluginMediaStreamTrack = self.pluginMediaStreamTracks[existingTrackId]

if pluginMediaStreamTrack == nil {
NSLog("WKWebViewRTC#MediaStreamTrack_clone() | ERROR: pluginMediaStreamTrack with id=%@ does not exist", String(existingTrackId))
return;
}

if self.pluginMediaStreams[newTrackId] == nil {
var rtcMediaStreamTrack = self.pluginMediaStreamTracks[existingTrackId]!.rtcMediaStreamTrack;
// twilio uses the sdp local description to map the track ids to the media id.
// if the original rtcMediaStreamTrack is not cloned, the rtcPeerConnection
// will not add the track and as such will not be found by Twilio.
// it is unable to do the mapping and find track and thus
// will not publish the local track.
if pluginMediaStreamTrack?.kind == "video" {
if let rtcVideoTrack = rtcMediaStreamTrack as? RTCVideoTrack{
NSLog("WKWebViewRTC#MediaStreamTrack_clone() cloning video source");
rtcMediaStreamTrack = self.rtcPeerConnectionFactory.videoTrack(with: rtcVideoTrack.source, trackId: newTrackId);
}
} else if pluginMediaStreamTrack?.kind == "audio" {
if let rtcAudioTrack = rtcMediaStreamTrack as? RTCAudioTrack{
NSLog("WKWebViewRTC#MediaStreamTrack_clone() cloning audio source");
rtcMediaStreamTrack = self.rtcPeerConnectionFactory.audioTrack(with: rtcAudioTrack.source, trackId: newTrackId);
}
}
let newPluginMediaStreamTrack = iMediaStreamTrack(rtcMediaStreamTrack: rtcMediaStreamTrack, trackId: newTrackId)

self.saveMediaStreamTrack(newPluginMediaStreamTrack)
} else {
NSLog("WKWebViewRTC#MediaStreamTrack_clone() | ERROR: pluginMediaStreamTrack with id=%@ already exist", String(newTrackId))
}
}

@objc(MediaStreamTrack_setListener:) func MediaStreamTrack_setListener(_ command: WkWebviewCommand) {
NSLog("WKWebViewRTC#MediaStreamTrack_setListener()")
Expand Down Expand Up @@ -851,7 +888,7 @@ public class WKWebViewRTC : NSObject {
},
eventListenerForEnded: { () -> Void in
// Remove the track from the container.
self.pluginMediaStreamTracks[pluginMediaStreamTrack!.id] = nil
self.deleteMediaStreamTrack(pluginMediaStreamTrack!);
}
)
}
Expand Down Expand Up @@ -977,13 +1014,26 @@ public class WKWebViewRTC : NSObject {
return;
}

let based64 = pluginMediaStreamRenderer!.save()
self.emit(command.callbackId,
result: WkWebviewCmdResult(
status:.WkWebviewCmdStatus_OK,
messageAs: based64
// Perform the task on a background queue.
DispatchQueue.global().async {
pluginMediaStreamRenderer!.save(
callback: { (data: String) -> Void in
DispatchQueue.main.async {
self.emit(command.callbackId,
result: WkWebviewCmdResult(
status:.WkWebviewCmdStatus_OK,
messageAs: data
)
)
}
},
errback: { (error: String) -> Void in
self.emit(command.callbackId,
result: WkWebviewCmdResult(status: .WkWebviewCmdStatus_ERROR, messageAs: error)
)
}
)
)
}
}

@objc(MediaStreamRenderer_close:) func MediaStreamRenderer_close(_ command: WkWebviewCommand) {
Expand Down Expand Up @@ -1114,7 +1164,7 @@ public class WKWebViewRTC : NSObject {
iRTCAudioController.selectAudioOutputSpeaker()
}

func dump(_ command: WkWebviewCommand) {
@objc(dump:) func dump(_ command: WkWebviewCommand) {
NSLog("WKWebViewRTC#dump()")

for (id, _) in self.pluginRTCPeerConnections {
Expand Down Expand Up @@ -1156,20 +1206,22 @@ public class WKWebViewRTC : NSObject {
}

// Store its PluginMediaStreamTracks' into the dictionary.
for (id, track) in pluginMediaStream.audioTracks {
if self.pluginMediaStreamTracks[id] == nil {
self.pluginMediaStreamTracks[id] = track
}
for (_, pluginMediaStreamTrack) in pluginMediaStream.audioTracks {
saveMediaStreamTrack(pluginMediaStreamTrack);
}
for (id, track) in pluginMediaStream.videoTracks {
if self.pluginMediaStreamTracks[id] == nil {
self.pluginMediaStreamTracks[id] = track
}

for (_, pluginMediaStreamTrack) in pluginMediaStream.videoTracks {
saveMediaStreamTrack(pluginMediaStreamTrack);
}
}

fileprivate func deleteMediaStream(_ id: String) {
self.pluginMediaStreams[id] = nil
fileprivate func deleteMediaStream(_ pluginMediaStream: iMediaStream) {
if (self.pluginMediaStreams[pluginMediaStream.id] != nil) {
self.pluginMediaStreams[pluginMediaStream.id] = nil

// deinit should call stop by itself
//pluginMediaStream.stop();
}
}

fileprivate func saveMediaStreamTrack(_ pluginMediaStreamTrack: iMediaStreamTrack) {
Expand All @@ -1178,10 +1230,51 @@ public class WKWebViewRTC : NSObject {
}
}

fileprivate func deleteMediaStreamTrack(_ id: String) {
self.pluginMediaStreamTracks[id] = nil
fileprivate func deleteMediaStreamTrack(_ pluginMediaStreamTrack: iMediaStreamTrack) {
if (self.pluginMediaStreamTracks[pluginMediaStreamTrack.id] != nil) {
self.pluginMediaStreamTracks[pluginMediaStreamTrack.id] = nil

// deinit should call stop by itself
//pluginMediaStreamTrack.stop();
}
}

fileprivate func cleanup() {

// Close all RTCPeerConnections
for (pcId, pluginRTCPeerConnection) in self.pluginRTCPeerConnections {
pluginRTCPeerConnection.close()
self.pluginRTCPeerConnections[pcId] = nil;
}

// Close all StreamRenderers
for (id, pluginMediaStreamRenderer) in self.pluginMediaStreamRenderers {
pluginMediaStreamRenderer.close()
self.pluginMediaStreamRenderers[id] = nil;
}

// Close All MediaStream
for (streamId, pluginMediaStream) in self.pluginMediaStreams {
// Store its PluginMediaStreamTracks' into the dictionary.
for (trackId, pluginMediaStreamTrack) in pluginMediaStream.audioTracks {
pluginMediaStream.removeTrack(pluginMediaStreamTrack);
deleteMediaStreamTrack(pluginMediaStreamTrack);
}

for (trackId, pluginMediaStreamTrack) in pluginMediaStream.videoTracks {
pluginMediaStream.removeTrack(pluginMediaStreamTrack);
deleteMediaStreamTrack(pluginMediaStreamTrack);
}

deleteMediaStream(pluginMediaStream);
}

// Close All MediaStreamTracks without MediaStream
for (trackId, pluginMediaStreamTrack) in self.pluginMediaStreamTracks {
deleteMediaStreamTrack(pluginMediaStreamTrack);
}
}

func native_console_log(didReceive message:WKScriptMessage)
{
print("console.log: \(message.body)")
Expand Down
24 changes: 15 additions & 9 deletions WKWebViewRTC/Classes/iEnumerateDevices.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* cordova-plugin-iosrtc v6.0.12
* cordova-plugin-iosrtc v6.0.17
* Cordova iOS plugin exposing the ̶f̶u̶l̶l̶ WebRTC W3C JavaScript APIs.
* Copyright 2015-2017 eFace2Face, Inc. (https://eface2face.com)
* Copyright 2015-2019 BasqueVoIPMafia (https://github.com/BasqueVoIPMafia)
Expand Down Expand Up @@ -60,8 +60,18 @@ class iEnumerateDevices {
fileprivate func getAllVideoDevices() -> [MediaDeviceInfo] {

var videoDevicesArr : [MediaDeviceInfo] = []
var deviceTypes: [AVCaptureDevice.DeviceType] = [.builtInTelephotoCamera, .builtInWideAngleCamera]
if #available(iOS 10.2, *) {
deviceTypes.append(.builtInDualCamera)

// Disabled tp prevent duplicate front camera
//if #available(iOS 11.1, *) {
// deviceTypes.append(.builtInTrueDepthCamera)
//}
}

let videoDevices: [AVCaptureDevice] = AVCaptureDevice.DiscoverySession.init(
deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera, AVCaptureDevice.DeviceType.builtInDualCamera],
deviceTypes: deviceTypes,
mediaType: AVMediaType.video,
position: AVCaptureDevice.Position.unspecified
).devices
Expand Down Expand Up @@ -124,9 +134,7 @@ fileprivate func getAllAudioDevices() -> [MediaDeviceInfo] {
var audioDevicesArr : [MediaDeviceInfo] = []
let audioInputDevices: [AVAudioSessionPortDescription] = audioSession.availableInputs!
var bluetoothDevice: AVAudioSessionPortDescription? = nil
var isBluetoothConnected : Bool = false
var wiredDevice: AVAudioSessionPortDescription? = nil
var isWiredConnected : Bool = false
var builtMicDevice: AVAudioSessionPortDescription? = nil

for audioInput in audioInputDevices {
Expand All @@ -146,21 +154,19 @@ fileprivate func getAllAudioDevices() -> [MediaDeviceInfo] {

if audioInput.portType == .bluetoothHFP || audioInput.portType == .bluetoothA2DP {
bluetoothDevice = audioInput
isBluetoothConnected = true
}

if audioInput.portType == .usbAudio || audioInput.portType == .headsetMic {
wiredDevice = audioInput
isWiredConnected = true
}
}

// Initialize audioInputSelected. Priority: [Wired - Wireless - Built-In Microphone]
if isWiredConnected {
if wiredDevice != nil {
iRTCAudioController.saveInputAudioDevice(inputDeviceUID: wiredDevice!.uid)
} else if isBluetoothConnected {
} else if bluetoothDevice != nil {
iRTCAudioController.saveInputAudioDevice(inputDeviceUID: bluetoothDevice!.uid)
} else {
} else if builtMicDevice != nil {
iRTCAudioController.saveInputAudioDevice(inputDeviceUID: builtMicDevice!.uid)
}
return audioDevicesArr
Expand Down
2 changes: 1 addition & 1 deletion WKWebViewRTC/Classes/iGetUserMedia.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* cordova-plugin-iosrtc v6.0.12
* cordova-plugin-iosrtc v6.0.17
* Cordova iOS plugin exposing the ̶f̶u̶l̶l̶ WebRTC W3C JavaScript APIs.
* Copyright 2015-2017 eFace2Face, Inc. (https://eface2face.com)
* Copyright 2015-2019 BasqueVoIPMafia (https://github.com/BasqueVoIPMafia)
Expand Down
Loading

0 comments on commit aa50c2e

Please sign in to comment.