Skip to content

Commit c2c59d4

Browse files
YPandasYuqi Huangsirknightj
authored
Release 1.1.0 (#110)
* updated ci.yml to include develop branch in the ci target (#109) Co-authored-by: Yuqi Huang <[email protected]> * WebRTC ingestion support (#107) * WebRTC ingestion support (master) * WebRTC ingestion support (joinStorageSession and joinStorageSessionAsViewer), new send video switch, toast notification for WebRTC connected, remove manual JoinStorageSession button, fullscreen ingestion view, updated ice candidate queue logic * Adjust to 3 total attempts per try * Fixing the UI display * Update the unit tests * Adjust the class name for view element from 'zz' to 'View' * Rename variables and method names according to comments * version bump from 1.0.0 to 1.1.0 (#108) Co-authored-by: Yuqi Huang <[email protected]> --------- Co-authored-by: Yuqi Huang <[email protected]> Co-authored-by: sirknightj <[email protected]>
1 parent c3d959c commit c2c59d4

File tree

11 files changed

+319
-75
lines changed

11 files changed

+319
-75
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ on:
44
push:
55
branches:
66
- master
7+
- develop
78
pull_request:
89
branches:
910
- master
11+
- develop
1012
jobs:
1113
build-sample-app:
1214
strategy:

Swift/AWSKinesisVideoWebRTCDemoApp.xcodeproj/project.pbxproj

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@
387387
17FC2F7B1E258FC500174015 = {
388388
CreatedOnToolsVersion = 7.3.1;
389389
LastSwiftMigration = 1020;
390+
ProvisioningStyle = Automatic;
390391
};
391392
703513E32399DA0400376B66 = {
392393
CreatedOnToolsVersion = 11.2;
@@ -469,6 +470,7 @@
469470
"${BUILT_PRODUCTS_DIR}/AWSCore/AWSCore.framework",
470471
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideo/AWSKinesisVideo.framework",
471472
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideoSignaling/AWSKinesisVideoSignaling.framework",
473+
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideoWebRTCStorage/AWSKinesisVideoWebRTCStorage.framework",
472474
"${BUILT_PRODUCTS_DIR}/AWSMobileClient/AWSMobileClient.framework",
473475
"${BUILT_PRODUCTS_DIR}/CommonCryptoModule/CommonCryptoModule.framework",
474476
"${PODS_ROOT}/GoogleWebRTC/Frameworks/frameworks/WebRTC.framework",
@@ -482,6 +484,7 @@
482484
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSCore.framework",
483485
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideo.framework",
484486
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideoSignaling.framework",
487+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideoWebRTCStorage.framework",
485488
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSMobileClient.framework",
486489
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CommonCryptoModule.framework",
487490
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
@@ -527,6 +530,7 @@
527530
"${BUILT_PRODUCTS_DIR}/AWSCore/AWSCore.framework",
528531
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideo/AWSKinesisVideo.framework",
529532
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideoSignaling/AWSKinesisVideoSignaling.framework",
533+
"${BUILT_PRODUCTS_DIR}/AWSKinesisVideoWebRTCStorage/AWSKinesisVideoWebRTCStorage.framework",
530534
"${BUILT_PRODUCTS_DIR}/AWSMobileClient/AWSMobileClient.framework",
531535
"${BUILT_PRODUCTS_DIR}/CommonCryptoModule/CommonCryptoModule.framework",
532536
"${PODS_ROOT}/GoogleWebRTC/Frameworks/frameworks/WebRTC.framework",
@@ -540,6 +544,7 @@
540544
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSCore.framework",
541545
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideo.framework",
542546
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideoSignaling.framework",
547+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSKinesisVideoWebRTCStorage.framework",
543548
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AWSMobileClient.framework",
544549
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CommonCryptoModule.framework",
545550
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
@@ -659,8 +664,8 @@
659664
isa = XCBuildConfiguration;
660665
buildSettings = {
661666
ALWAYS_SEARCH_USER_PATHS = YES;
662-
"ARCHS[sdk=iphonesimulator*]" = x86_64;
663667
"ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)";
668+
"ARCHS[sdk=iphonesimulator*]" = x86_64;
664669
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
665670
CLANG_ANALYZER_NONNULL = YES;
666671
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -723,8 +728,8 @@
723728
isa = XCBuildConfiguration;
724729
buildSettings = {
725730
ALWAYS_SEARCH_USER_PATHS = YES;
726-
"ARCHS[sdk=iphonesimulator*]" = x86_64;
727731
"ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)";
732+
"ARCHS[sdk=iphonesimulator*]" = x86_64;
728733
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
729734
CLANG_ANALYZER_NONNULL = YES;
730735
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -784,6 +789,8 @@
784789
baseConfigurationReference = BDB1499CD0982C181C7DDE9E /* Pods-AWSKinesisVideoWebRTCDemoApp.debug.xcconfig */;
785790
buildSettings = {
786791
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
792+
CODE_SIGN_IDENTITY = "Apple Development";
793+
CODE_SIGN_STYLE = Automatic;
787794
DEVELOPMENT_TEAM = "";
788795
ENABLE_BITCODE = NO;
789796
"EXCLUDED_ARCHS[sdk=*]" = "";
@@ -825,6 +832,7 @@
825832
);
826833
PRODUCT_BUNDLE_IDENTIFIER = com.kinesisvideo.KVSApp1;
827834
PRODUCT_NAME = "$(TARGET_NAME)";
835+
PROVISIONING_PROFILE_SPECIFIER = "";
828836
SWIFT_VERSION = 4.0;
829837
};
830838
name = Debug;

Swift/AWSKinesisVideoWebRTCDemoAppTests/VideoViewControllerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ class VideoViewControllerTests: XCTestCase{
2020
signalingClient!.connect()
2121

2222
RTCIceServersList.append(RTCIceServer.init(urlStrings: ["stun:stun.kinesisvideo." + "us-west-2" + ".amazonaws.com:443"]))
23-
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: true)
23+
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: true, isVideoOn: true)
2424
webRTCClient!.delegate = channelVC
25-
videoViewController = VideoViewController(webRTCClient: self.webRTCClient!, signalingClient: self.signalingClient!, localSenderClientID: "randomClientID", isMaster: true, mediaServerEndPoint: nil)
25+
videoViewController = VideoViewController(webRTCClient: self.webRTCClient!, signalingClient: self.signalingClient!, localSenderClientID: "randomClientID", isMaster: true, signalingChannelArn: nil)
2626
}
2727

2828
override func tearDown() {

Swift/KVSiOSApp/ChannelConfigurationViewController.swift

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import AWSCore
22
import AWSCognitoIdentityProvider
33
import AWSKinesisVideo
44
import AWSKinesisVideoSignaling
5+
import AWSKinesisVideoWebRTCStorage
56
import AWSMobileClient
67
import Foundation
78
import WebRTC
@@ -32,6 +33,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
3233

3334
// variables controlled by UI
3435
var sendAudioEnabled: Bool = true
36+
var sendVideoEnabled: Bool = true
3537
var isMaster: Bool = false
3638
var signalingConnected: Bool = false
3739
var selectedResolution: VideoResolution = .resolution720p
@@ -52,6 +54,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
5254
@IBOutlet var clientID: UITextField!
5355
@IBOutlet var regionName: UITextField!
5456
@IBOutlet var isAudioEnabled: UISwitch!
57+
@IBOutlet var isVideoEnabled: UISwitch!
5558
@IBOutlet var resolutionButton: UIButton!
5659

5760
// Connect Buttons
@@ -118,6 +121,14 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
118121
}
119122
}
120123

124+
@IBAction func videoStateChanged(sender: UISwitch!) {
125+
if sender.isOn {
126+
self.sendVideoEnabled = true
127+
} else {
128+
self.sendVideoEnabled = false
129+
}
130+
}
131+
121132
@IBAction func resolutionButtonTapped(_ sender: UIButton) {
122133
let alert = UIAlertController(title: "Select Resolution", message: nil, preferredStyle: .actionSheet)
123134

@@ -224,18 +235,24 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
224235
}
225236
}
226237
// check whether signalling channel will save its recording to a stream
227-
// only applies for master
228-
var usingMediaServer : Bool = false
229-
if self.isMaster {
230-
usingMediaServer = isUsingMediaServer(channelARN: channelARN!, channelName: channelNameValue)
231-
// Make sure that audio is enabled if ingesting webrtc connection
232-
if(usingMediaServer && !self.sendAudioEnabled) {
233-
popUpError(title: "Invalid Configuration", message: "Audio must be enabled to use MediaServer")
238+
var useStorageSession: Bool = isUsingStorageSession(channelARN: channelARN!, channelName: channelNameValue)
239+
// Make sure that audio is enabled if ingesting webrtc connection
240+
if (useStorageSession) {
241+
if (self.isMaster && (!self.isAudioEnabled.isOn || !self.isVideoEnabled.isOn)) {
242+
// Master mode: Both audio and video required
243+
popUpError(title: "Invalid Configuration", message: "Video and audio must be enabled for WebRTC ingestion master")
234244
return
245+
} else if (!self.isMaster) {
246+
// Viewer mode: Video not allowed, audio optional
247+
if (self.isVideoEnabled.isOn) {
248+
popUpError(title: "Invalid Configuration", message: "Video is not allowed for WebRTC ingestion viewer")
249+
return
250+
}
235251
}
236252
}
253+
237254
// get signalling channel endpoints
238-
let endpoints = getSignallingEndpoints(channelARN: channelARN!, region: awsRegionValue, isMaster: self.isMaster, useMediaServer: usingMediaServer)
255+
let endpoints = getSignallingEndpoints(channelARN: channelARN!, region: awsRegionValue, isMaster: self.isMaster, useStorageSession: useStorageSession)
239256
//// Ensure that the WebSocket (WSS) endpoint is available; WebRTC requires a valid signaling endpoint.
240257
if endpoints["WSS"] == nil {
241258
popUpError(title: "Invalid SignallingEndpoints", message: "SignallingEndpoints is required for WebRTC connection")
@@ -254,8 +271,20 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
254271
service: .KinesisVideo,
255272
url: URL(string: endpoints["HTTPS"]!!))
256273
let RTCIceServersList = getIceCandidates(channelARN: channelARN!, endpoint: httpsEndpoint!, regionType: awsRegionType, clientId: localSenderId)
257-
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: sendAudioEnabled, resolution: selectedResolution)
274+
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: sendAudioEnabled, isVideoOn: sendVideoEnabled, resolution: selectedResolution)
258275
webRTCClient!.delegate = self
276+
277+
guard !useStorageSession || endpoints["WEBRTC"] != nil else {
278+
print("connectAsRole IllegalState! WEBRTC endpoint is required for WebRTC ingestion")
279+
return
280+
}
281+
if useStorageSession {
282+
let webRTCEndpoint: String = endpoints["WEBRTC"]!!
283+
let webRTCStorageConfiguration = AWSServiceConfiguration(region: awsRegionType,
284+
endpoint: AWSEndpoint(urlString: webRTCEndpoint),
285+
credentialsProvider: getCredentialsProvider())
286+
AWSKinesisVideoWebRTCStorage.register(with: webRTCStorageConfiguration!, forKey: awsKinesisVideoKey)
287+
}
259288

260289
// Connect to signalling channel with wss endpoint
261290
print("Connecting to web socket from channel config")
@@ -267,7 +296,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
267296
let seconds = 2.0
268297
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
269298
self.updateConnectionLabel()
270-
self.vc = VideoViewController(webRTCClient: self.webRTCClient!, signalingClient: self.signalingClient!, localSenderClientID: self.localSenderId, isMaster: self.isMaster, mediaServerEndPoint: endpoints["WEBRTC"] ?? nil)
299+
self.vc = VideoViewController(webRTCClient: self.webRTCClient!, signalingClient: self.signalingClient!, localSenderClientID: self.localSenderId, isMaster: self.isMaster, signalingChannelArn: useStorageSession ? channelARN : nil, isVideoEnabled: self.sendVideoEnabled)
271300
self.present(self.vc!, animated: true, completion: nil)
272301
}
273302
}
@@ -327,8 +356,8 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
327356
return channelARN
328357
}
329358

330-
// check media server is enabled for signalling channel
331-
func isUsingMediaServer(channelARN: String, channelName: String) -> Bool {
359+
// check storage session (WebRTC ingestion) is enabled for signalling channel
360+
func isUsingStorageSession(channelARN: String, channelName: String) -> Bool {
332361
var usingMediaServer : Bool = false
333362
/*
334363
equivalent AWS CLI command:
@@ -348,6 +377,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
348377
}
349378
}
350379
}).waitUntilFinished()
380+
print("\(channelARN) configured for ingestion? \(usingMediaServer)")
351381
return usingMediaServer
352382
}
353383

@@ -388,7 +418,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
388418
}
389419

390420
// Get signalling endpoints for the given signalling channel ARN
391-
func getSignallingEndpoints(channelARN: String, region: String, isMaster: Bool, useMediaServer: Bool) -> Dictionary<String, String?> {
421+
func getSignallingEndpoints(channelARN: String, region: String, isMaster: Bool, useStorageSession: Bool) -> Dictionary<String, String?> {
392422

393423
var endpoints = Dictionary <String, String?>()
394424
/*
@@ -400,7 +430,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
400430
singleMasterChannelEndpointConfiguration?.protocols = videoProtocols
401431
singleMasterChannelEndpointConfiguration?.role = getSingleMasterChannelEndpointRole(isMaster: isMaster)
402432

403-
if(useMediaServer){
433+
if (useStorageSession){
404434
singleMasterChannelEndpointConfiguration?.protocols?.append("WEBRTC")
405435
}
406436

@@ -499,6 +529,12 @@ extension ChannelConfigurationViewController: SignalClientDelegate {
499529
remoteSenderClientId = senderClientId
500530
}
501531
setRemoteSenderClientId()
532+
533+
// Mark that offer was received to stop storage session retries
534+
if sdp.type == .offer {
535+
vc?.markOfferReceived()
536+
}
537+
502538
webRTCClient!.set(remoteSdp: sdp, clientId: senderClientId) { _ in
503539
print("Setting remote sdp and sending answer.")
504540
self.vc!.sendAnswer(recipientClientID: self.remoteSenderClientId!)
@@ -529,14 +565,23 @@ extension ChannelConfigurationViewController: WebRTCClientDelegate {
529565
switch state {
530566
case .connected, .completed:
531567
print("WebRTC connected/completed state")
568+
DispatchQueue.main.async {
569+
self.vc?.showToast(message: "WebRTC Connected", length: "short")
570+
}
532571
case .disconnected:
533572
print("WebRTC disconnected state")
573+
DispatchQueue.main.async {
574+
self.vc?.showToast(message: "WebRTC Disconnected", length: "short")
575+
}
576+
case .failed:
577+
print("WebRTC failed state")
578+
DispatchQueue.main.async {
579+
self.vc?.showToast(message: "WebRTC Connection Failed", length: "long")
580+
}
534581
case .new:
535582
print("WebRTC new state")
536583
case .checking:
537584
print("WebRTC checking state")
538-
case .failed:
539-
print("WebRTC failed state")
540585
case .closed:
541586
print("WebRTC closed state")
542587
case .count:

Swift/KVSiOSApp/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let cognitoIdentityPoolId = "REPLACEME"
1212

1313
// App constants
1414
let appName = "aws-kvs-webrtc-ios-client"
15-
let appVersion = "1.0.0"
15+
let appVersion = "1.1.0"
1616

1717
// KinesisVideo constants
1818
let awsKinesisVideoKey = "kinesisvideo"

0 commit comments

Comments
 (0)