Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion Swift/KVSiOSApp/ChannelConfigurationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import AWSMobileClient
import Foundation
import WebRTC

enum VideoResolution: String, CaseIterable {
case resolution240p = "320x240"
case resolution480p = "640x480"
case resolution720p = "1280x720"
case resolution1080p = "1920x1080"

var dimensions: (width: Int32, height: Int32) {
switch self {
case .resolution240p: return (320, 240)
case .resolution480p: return (640, 480)
case .resolution720p: return (1280, 720)
case .resolution1080p: return (1920, 1080)
}
}
}

class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate {

// cognito credentials
Expand All @@ -18,6 +34,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
var sendAudioEnabled: Bool = true
var isMaster: Bool = false
var signalingConnected: Bool = false
var selectedResolution: VideoResolution = .resolution720p

// clients for WEBRTC Connection
var signalingClient: SignalingClient?
Expand All @@ -35,6 +52,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
@IBOutlet var clientID: UITextField!
@IBOutlet var regionName: UITextField!
@IBOutlet var isAudioEnabled: UISwitch!
@IBOutlet var resolutionButton: UIButton!

// Connect Buttons
@IBOutlet weak var connectAsMasterButton: UIButton!
Expand Down Expand Up @@ -73,6 +91,8 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
channelName.delegate = self
clientID.delegate = self
regionName.delegate = self

resolutionButton.setTitle(selectedResolution.rawValue, for: .normal)
}

func textFieldShouldReturn(_: UITextField) -> Bool {
Expand All @@ -98,6 +118,27 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
}
}

@IBAction func resolutionButtonTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "Select Resolution", message: nil, preferredStyle: .actionSheet)

for resolution in VideoResolution.allCases {
let action = UIAlertAction(title: resolution.rawValue, style: .default) { _ in
self.selectedResolution = resolution
sender.setTitle(resolution.rawValue, for: .normal)
}
alert.addAction(action)
}

alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))

if let popover = alert.popoverPresentationController {
popover.sourceView = sender
popover.sourceRect = sender.bounds
}

present(alert, animated: true)
}

@IBAction func connectAsViewer(_sender _: AnyObject) {
self.isMaster = false
connectAsRole()
Expand Down Expand Up @@ -211,7 +252,7 @@ class ChannelConfigurationViewController: UIViewController, UITextFieldDelegate
service: .KinesisVideo,
url: URL(string: endpoints["HTTPS"]!!))
let RTCIceServersList = getIceCandidates(channelARN: channelARN!, endpoint: httpsEndpoint!, regionType: awsRegionType, clientId: localSenderId)
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: sendAudioEnabled)
webRTCClient = WebRTCClient(iceServers: RTCIceServersList, isAudioOn: sendAudioEnabled, resolution: selectedResolution)
webRTCClient!.delegate = self

// Connect to signalling channel with wss endpoint
Expand Down
17 changes: 16 additions & 1 deletion Swift/KVSiOSApp/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,22 @@
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3k1-42-Mbg">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="res-btn-123">
<rect key="frame" x="0.0" y="204" width="243" height="25"/>
<color key="backgroundColor" systemColor="systemGrayColor"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="res-height-123"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="1280x720">
<color key="titleColor" systemColor="labelColor"/>
</state>
<connections>
<action selector="resolutionButtonTapped:" destination="fxY-gB-7Yj" eventType="touchUpInside" id="res-action-123"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3k1-42-Mbg">
<rect key="frame" x="0.0" y="272" width="250" height="25"/>
<color key="backgroundColor" red="0.1960784314" green="0.60392156860000001" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="connectasmasterbutton"/>
<constraints>
Expand Down Expand Up @@ -672,6 +686,7 @@
<outlet property="connectedLabel" destination="NOw-C6-Bie" id="cMG-kR-3E9"/>
<outlet property="isAudioEnabled" destination="uWn-mC-EnS" id="CUs-sB-mgl"/>
<outlet property="regionName" destination="nr9-tN-CuW" id="tX6-zd-Kpw"/>
<outlet property="resolutionButton" destination="res-btn-123" id="res-outlet-123"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7NB-jG-Ywn" userLabel="First Responder" sceneMemberID="firstResponder"/>
Expand Down
46 changes: 36 additions & 10 deletions Swift/KVSiOSApp/WebRTCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ final class WebRTCClient: NSObject {

weak var delegate: WebRTCClientDelegate?
private let peerConnection: RTCPeerConnection
private var videoSource: RTCVideoSource?

// Accept video and audio from remote peer
private let streamId = "KvsLocalMediaStream"
Expand All @@ -28,11 +29,14 @@ final class WebRTCClient: NSObject {
private var remoteVideoTrack: RTCVideoTrack?
private var remoteDataChannel: RTCDataChannel?
private var constructedIceServers: [RTCIceServer]?
private var selectedResolution: VideoResolution

private var peerConnectionFoundMap = [String: RTCPeerConnection]()
private var pendingIceCandidatesMap = [String: Set<RTCIceCandidate>]()

required init(iceServers: [RTCIceServer], isAudioOn: Bool) {
required init(iceServers: [RTCIceServer], isAudioOn: Bool, resolution: VideoResolution = .resolution720p) {
self.selectedResolution = resolution

let config = RTCConfiguration()
config.iceServers = iceServers
config.sdpSemantics = .unifiedPlan
Expand Down Expand Up @@ -183,15 +187,27 @@ final class WebRTCClient: NSObject {
return
}

guard
let frontCamera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == .front }),
guard let frontCamera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == .front }) else {
print("Unable to find the front camera (selfie camera)")
return
}

let format = RTCCameraVideoCapturer.supportedFormats(for: frontCamera).last,
let targetDimensions = selectedResolution.dimensions
let formats = RTCCameraVideoCapturer.supportedFormats(for: frontCamera)

let fps = format.videoSupportedFrameRateRanges.first?.maxFrameRate else {
debugPrint("Error setting fps.")
return
}
let selectedFormat = formats.first { format in
let dims = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
return dims.width == targetDimensions.width && dims.height == targetDimensions.height
} ?? formats.first

guard let format = selectedFormat,
let fps = format.videoSupportedFrameRateRanges.first?.maxFrameRate else {
debugPrint("No suitable format found")
return
}

let dims = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
print("Selected camera format: \(dims.width)x\(dims.height) at \(Int(fps.magnitude)) fps")

capturer.startCapture(with: frontCamera,
format: format,
Expand Down Expand Up @@ -225,8 +241,8 @@ final class WebRTCClient: NSObject {

private func createVideoTrack() -> RTCVideoTrack {
let videoSource = WebRTCClient.factory.videoSource()
videoSource.adaptOutputFormat(toWidth: 1280, height: 720, fps: 30)
videoCapturer = RTCCameraVideoCapturer(delegate: videoSource)
self.videoSource = videoSource
videoCapturer = RTCCameraVideoCapturer(delegate: self)
return WebRTCClient.factory.videoTrack(with: videoSource, trackId: "KvsVideoTrack")
}

Expand Down Expand Up @@ -278,6 +294,16 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
}
}

extension WebRTCClient: RTCVideoCapturerDelegate {
func capturer(_ capturer: RTCVideoCapturer, didCapture frame: RTCVideoFrame) {
videoSource?.capturer(capturer, didCapture: frame)

// If you want to rotate the frame:
// let rotatedFrame = RTCVideoFrame(buffer: frame.buffer, rotation: ._0, timeStampNs: frame.timeStampNs)
// videoSource?.capturer(capturer, didCapture: rotatedFrame)
}
}

extension WebRTCClient: RTCDataChannelDelegate {
func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
debugPrint("dataChannel didChangeState: \(dataChannel.readyState)")
Expand Down