From 6520858a5415fe157b6bd6894b485989ec7c8507 Mon Sep 17 00:00:00 2001 From: gzerad Date: Fri, 9 Sep 2022 12:44:19 +0300 Subject: [PATCH] 0.4.1 Release --- .../Meeting/Chat/ChatViewController.swift | 7 +- .../Meeting/HLSStreamViewController.swift | 77 ++++++++++++++++++- .../Meeting/HMSSDKInteractor.swift | 9 ++- .../Meeting/MeetingViewController.swift | 55 +++++++++++-- .../Meeting/MeetingViewModel.swift | 6 ++ .../Menus/RoomStateViewController.swift | 20 ++--- .../HMSSDKExample/Meeting/PeerMetadata.swift | 2 +- .../Meeting/PiP/PiPController.swift | 6 ++ .../HMSSDKExample/Meeting/PiP/PiPView.swift | 8 +- .../Meeting/RolePreviewViewController.swift | 4 +- HMSSDK.podspec | 6 +- Package.swift | 4 +- 12 files changed, 165 insertions(+), 39 deletions(-) diff --git a/Example/HMSSDKExample/Meeting/Chat/ChatViewController.swift b/Example/HMSSDKExample/Meeting/Chat/ChatViewController.swift index 4427e88..f68eb01 100644 --- a/Example/HMSSDKExample/Meeting/Chat/ChatViewController.swift +++ b/Example/HMSSDKExample/Meeting/Chat/ChatViewController.swift @@ -151,7 +151,7 @@ final class ChatViewController: UIViewController { sender.isEnabled = false - let messageHandler: ((HMSMessage?, HMSError?) -> Void) = { [weak self, weak sender] sentMessage, error in + let messageHandler: ((HMSMessage?, Error?) -> Void) = { [weak self, weak sender] sentMessage, error in sender?.isEnabled = true if let sentMessage = sentMessage { @@ -171,11 +171,12 @@ final class ChatViewController: UIViewController { } } - private func showMessageSendError(_ error: HMSError) { + private func showMessageSendError(_ error: Error) { + guard let error = error as? HMSError else { return } let title = "Could Not Send a Message" let alertController = UIAlertController(title: title, - message: error.message, + message: error.localizedDescription, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .cancel)) diff --git a/Example/HMSSDKExample/Meeting/HLSStreamViewController.swift b/Example/HMSSDKExample/Meeting/HLSStreamViewController.swift index 83a52b7..96a46ed 100644 --- a/Example/HMSSDKExample/Meeting/HLSStreamViewController.swift +++ b/Example/HMSSDKExample/Meeting/HLSStreamViewController.swift @@ -9,11 +9,16 @@ import UIKit import AVFoundation -class HLSStreamViewController: UIViewController { +class HLSStreamViewController: UIViewController, AVPlayerItemMetadataCollectorPushDelegate { var player: AVPlayer? var playerView: PlayerView! + var metadataView: UILabel! + var metadataCollector: AVPlayerItemMetadataCollector! var retriesLeft = 0 + var playerItem: AVPlayerItem? + var currentMetadataGroup: AVDateRangeMetadataGroup? + var metadataGroups = [AVDateRangeMetadataGroup]() var streamURL: URL? { didSet { @@ -26,12 +31,25 @@ class HLSStreamViewController: UIViewController { } override func loadView() { + let containerView = UIView() + view = containerView + playerView = PlayerView() - view = playerView + containerView.addConstrained(subview: playerView) + + metadataView = UILabel() + containerView.addSubview(metadataView) + metadataView.translatesAutoresizingMaskIntoConstraints = false + metadataView.backgroundColor = .lightGray + metadataView.textColor = .black + metadataView.textAlignment = .center + metadataView.bottomAnchor.constraint(equalTo: playerView.bottomAnchor).isActive = true + metadataView.leftAnchor.constraint(equalTo: playerView.leftAnchor).isActive = true + metadataView.rightAnchor.constraint(equalTo: playerView.rightAnchor).isActive = true + metadataView.heightAnchor.constraint(equalToConstant: 60.0).isActive = true + metadataView.isHidden = true } - var playerItem: AVPlayerItem? - override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) @@ -58,10 +76,15 @@ class HLSStreamViewController: UIViewController { let assetKeys = [ "playable" ] + + metadataCollector = AVPlayerItemMetadataCollector() + metadataCollector.setDelegate(self, queue: DispatchQueue.main) + // Create a new AVPlayerItem with the asset and an // array of asset keys to be automatically loaded let item = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: assetKeys) + item.add(metadataCollector) // Register as an observer of the player item's status property item.addObserver(self, @@ -73,6 +96,9 @@ class HLSStreamViewController: UIViewController { if player == nil { player = AVPlayer(playerItem: item) playerView.player = player + player?.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: .main, using: { [weak self] time in + self?.updateMetadataView(for: time) + }) } else { player?.replaceCurrentItem(with: item) } @@ -81,6 +107,35 @@ class HLSStreamViewController: UIViewController { func stop() { player?.pause() } + + func updateMetadataView(for currentTime: CMTime) { + hideCurrentMetadataViewIfNeeded() + + guard currentMetadataGroup == nil, let playerItem = playerItem else { return } + + for group in metadataGroups { + if group.shouldShow(for: playerItem) { + showMetadataView(for: group) + break + } + } + } + + func showMetadataView(for group: AVDateRangeMetadataGroup) { + guard currentMetadataGroup != group else { return } + + currentMetadataGroup = group + metadataView.isHidden = false + metadataView.text = group.items.first?.stringValue + } + + func hideCurrentMetadataViewIfNeeded() { + guard let currentMetadataGroup = currentMetadataGroup, + let playerItem = playerItem, + !currentMetadataGroup.shouldShow(for: playerItem) else { return } + self.currentMetadataGroup = nil + metadataView.isHidden = true + } override func observeValue(forKeyPath keyPath: String?, of object: Any?, @@ -132,6 +187,13 @@ class HLSStreamViewController: UIViewController { @objc func applicationDidBecomeActive(_ notificiation: Notification) { playerView.player = player } + + func metadataCollector(_ metadataCollector: AVPlayerItemMetadataCollector, + didCollect metadataGroups: [AVDateRangeMetadataGroup], + indexesOfNewGroups: IndexSet, + indexesOfModifiedGroups: IndexSet) { + self.metadataGroups = metadataGroups + } } class PlayerView: UIView { @@ -148,3 +210,10 @@ class PlayerView: UIView { } } } + +extension AVDateRangeMetadataGroup { + func shouldShow(for item: AVPlayerItem) -> Bool { + guard let endDate = endDate, let currentDate = item.currentDate() else { return false } + return startDate <= currentDate && currentDate < endDate + } +} diff --git a/Example/HMSSDKExample/Meeting/HMSSDKInteractor.swift b/Example/HMSSDKExample/Meeting/HMSSDKInteractor.swift index 774f1bb..4964ebb 100644 --- a/Example/HMSSDKExample/Meeting/HMSSDKInteractor.swift +++ b/Example/HMSSDKExample/Meeting/HMSSDKInteractor.swift @@ -206,20 +206,21 @@ final class HMSSDKInteractor: HMSUpdateListener { if let audio = track as? HMSAudioTrack { updatedMuteStatus?(audio) } - if let videoTrack = track as? HMSVideoTrack, videoTrack.source == HMSCommonTrackSource.screen { + if let videoTrack = track as? HMSRemoteVideoTrack, videoTrack.source == HMSCommonTrackSource.screen { if update == .trackAdded { pipController.set(screenTrack: videoTrack) } - else { + else if update == .trackRemoved { pipController.remove(screenTrack: videoTrack) } } } - func on(error: HMSError) { + func on(error: Error) { + guard let error = error as? HMSError else { return } NotificationCenter.default.post(name: Constants.gotError, object: nil, - userInfo: ["error": error.message]) + userInfo: ["error": "\(error.localizedDescription)"]) } func on(message: HMSMessage) { diff --git a/Example/HMSSDKExample/Meeting/MeetingViewController.swift b/Example/HMSSDKExample/Meeting/MeetingViewController.swift index 411d521..313e5a1 100644 --- a/Example/HMSSDKExample/Meeting/MeetingViewController.swift +++ b/Example/HMSSDKExample/Meeting/MeetingViewController.swift @@ -490,7 +490,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { image: UIImage(systemName: "stop.circle")) { [weak self] _ in guard let self = self else { return } self.interactor?.hmsSDK?.stopRTMPAndRecording { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Stop RTMP/Recording") return } @@ -511,7 +511,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { image: UIImage(systemName: "stop.circle")) { [weak self] _ in guard let self = self else { return } self.interactor?.hmsSDK?.stopHLSStreaming { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Stop HLS") return } @@ -519,6 +519,13 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { } } actions.append(stopHLS) + + let sendMetadata = UIAction(title: "Send HLS Timed Metadata", + image: UIImage(systemName: "tray.and.arrow.up.fill")) { [weak self] _ in + guard let self = self else { return } + self.showMetadataPrompt() + } + actions.append(sendMetadata) if interactor.canEndRoom { let endRoomAction = UIAction(title: "End Room", @@ -526,7 +533,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { guard let self = self else { return } self.interactor?.hmsSDK?.endRoom(reason: "Meeting Ended") { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "End Room") return } @@ -610,7 +617,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { } self?.interactor.hmsSDK?.change(name: name, completion: { _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Change name") } else { UserDefaults.standard.set(name, forKey: Constants.defaultName) @@ -620,6 +627,36 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { present(alertController, animated: true) } + + private func showMetadataPrompt() { + let title = "Enter metadata to send" + let action = "Send" + + let alertController = UIAlertController(title: title, + message: nil, + preferredStyle: .alert) + + alertController.addTextField { textField in + textField.clearButtonMode = .always + } + + alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + alertController.addAction(UIAlertAction(title: action, style: .default) { [weak self] _ in + guard let metadataPayload = alertController.textFields?[0].text, !metadataPayload.isEmpty else { + return + } + + let metadata = HMSHLSTimedMetadata(payload: metadataPayload, duration: 5) + + self?.interactor.hmsSDK?.sendHLSTimedMetadata([metadata], completion: { _, error in + if let error = error as? HMSError { + self?.showActionError(error, action: "Send Metadata") + } + }) + }) + + present(alertController, animated: true) + } private func showMutePrompt() { @@ -733,6 +770,8 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { private func handle(removedFromRoom notification: HMSRemovedFromRoomNotification) { let title = "\(notification.requestedBy?.name ?? "100ms app") removed you from this room: \(notification.reason)" + + interactor.pipController.roomEnded(reason: title) let alertController = UIAlertController(title: title, message: nil, @@ -783,7 +822,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { let title = "Could Not \(action)" let alertController = UIAlertController(title: title, - message: error.message, + message: error.localizedDescription, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .cancel)) @@ -880,7 +919,7 @@ final class MeetingViewController: UIViewController, UIDocumentPickerDelegate { sender.isSelected = !sender.isSelected let meta = PeerMetadata(isHandRaised: sender.isSelected) interactor?.hmsSDK?.change(metadataObject: meta) { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Raise hand") } } @@ -932,7 +971,7 @@ extension MeetingViewController: ChangeAllRoleViewControllerDelegate { extension MeetingViewController: RTMPSettingsViewControllerDelegate { func rtmpSettingsController(_ rtmpSettingsController: RTMPSettingsViewController, didSelect config: HMSRTMPConfig) { interactor?.hmsSDK?.startRTMPOrRecording(config: config) { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Start RTMP/Recording") return } @@ -945,7 +984,7 @@ extension MeetingViewController: RTMPSettingsViewControllerDelegate { extension MeetingViewController: HLSSettingsViewControllerDelegate { func hlsSettingsController(_ hlsSettingsController: HLSSettingsViewController, didSelect config: HMSHLSConfig?) { interactor?.hmsSDK?.startHLSStreaming(config: config) { [weak self] _, error in - if let error = error { + if let error = error as? HMSError { self?.showActionError(error, action: "Start HLS Streaming") return } diff --git a/Example/HMSSDKExample/Meeting/MeetingViewModel.swift b/Example/HMSSDKExample/Meeting/MeetingViewModel.swift index 01ceb3e..4800a41 100644 --- a/Example/HMSSDKExample/Meeting/MeetingViewModel.swift +++ b/Example/HMSSDKExample/Meeting/MeetingViewModel.swift @@ -394,6 +394,7 @@ final class MeetingViewModel: NSObject, cell.videoView.mirror = true cell.videoView.videoContentMode = .scaleAspectFill + cell.videoView.isZoomAndPanEnabled = false if viewModel.peer is HMSRemotePeer { @@ -405,6 +406,11 @@ final class MeetingViewModel: NSObject, } else { cell.moreButton.isHidden = false } + + // Let's enable panning and zooming in screen share view + if viewModel.videoTrack?.source == HMSCommonTrackSource.screen { + cell.videoView.isZoomAndPanEnabled = true + } } } diff --git a/Example/HMSSDKExample/Meeting/Menus/RoomStateViewController.swift b/Example/HMSSDKExample/Meeting/Menus/RoomStateViewController.swift index 65613fb..b1b508e 100644 --- a/Example/HMSSDKExample/Meeting/Menus/RoomStateViewController.swift +++ b/Example/HMSSDKExample/Meeting/Menus/RoomStateViewController.swift @@ -60,9 +60,9 @@ class RoomStateViewController: FormViewController { } } - if let error = room.browserRecordingState.error { + if let error = room.browserRecordingState.error as? HMSError { section <<< LabelRow() { - $0.title = "Error: \(error.message) (\(error.code))" + $0.title = "Error: \(error.localizedDescription)" } } form +++ section @@ -78,9 +78,9 @@ class RoomStateViewController: FormViewController { $0.title = "Started at: \(startDate)" } } - if let error = room.serverRecordingState.error { + if let error = room.serverRecordingState.error as? HMSError { section <<< LabelRow() { - $0.title = "Error: \(error.message) (\(error.code))" + $0.title = "Error: \(error.localizedDescription)" } } form +++ section @@ -105,9 +105,9 @@ class RoomStateViewController: FormViewController { } } - if let error = room.hlsRecordingState.error { + if let error = room.hlsRecordingState.error as? HMSError { section <<< LabelRow() { - $0.title = "Error: \(error.message) (\(error.code))" + $0.title = "Error: \(error.localizedDescription)" } } @@ -124,9 +124,9 @@ class RoomStateViewController: FormViewController { $0.title = "Started at: \(startDate)" } } - if let error = room.rtmpStreamingState.error { + if let error = room.rtmpStreamingState.error as? HMSError { section <<< LabelRow() { - $0.title = "Error: \(error.message) (\(error.code))" + $0.title = "Error: \(error.localizedDescription)" } } form +++ section @@ -149,9 +149,9 @@ class RoomStateViewController: FormViewController { count += 1 } - if let error = room.rtmpStreamingState.error { + if let error = room.rtmpStreamingState.error as? HMSError { section <<< LabelRow() { - $0.title = "Error: \(error.message) (\(error.code))" + $0.title = "Error: \(error.localizedDescription)" } } form +++ section diff --git a/Example/HMSSDKExample/Meeting/PeerMetadata.swift b/Example/HMSSDKExample/Meeting/PeerMetadata.swift index 2354ba1..aff5a00 100644 --- a/Example/HMSSDKExample/Meeting/PeerMetadata.swift +++ b/Example/HMSSDKExample/Meeting/PeerMetadata.swift @@ -29,7 +29,7 @@ extension HMSPeer { } extension HMSSDK { - func change(metadataObject: PeerMetadata, completion: ((Bool, HMSError?) -> Void)? = nil) { + func change(metadataObject: PeerMetadata, completion: ((Bool, Error?) -> Void)? = nil) { guard let data = try? JSONEncoder().encode(metadataObject), let dataString = String(data: data, encoding: .utf8) else { completion?(false, nil) diff --git a/Example/HMSSDKExample/Meeting/PiP/PiPController.swift b/Example/HMSSDKExample/Meeting/PiP/PiPController.swift index f1f4fad..bae9c3a 100644 --- a/Example/HMSSDKExample/Meeting/PiP/PiPController.swift +++ b/Example/HMSSDKExample/Meeting/PiP/PiPController.swift @@ -17,6 +17,7 @@ class PiPModel: ObservableObject { @Published var isVideoActive = false @Published var pipViewEnabled = false + @Published var roomEndedString: String? } class PiPController: NSObject { @@ -103,6 +104,7 @@ class PiPController: NSObject { } func set(screenTrack: HMSVideoTrack) { + model.screenTrack = screenTrack pipVideoCallViewController?.preferredContentSize = .init(width: 1920, height: 1080) } @@ -118,6 +120,10 @@ class PiPController: NSObject { func stopPiP() { self.pipController?.stopPictureInPicture() } + + func roomEnded(reason: String) { + model.roomEndedString = reason + } } extension PiPController: AVPictureInPictureControllerDelegate { diff --git a/Example/HMSSDKExample/Meeting/PiP/PiPView.swift b/Example/HMSSDKExample/Meeting/PiP/PiPView.swift index 3c4d77d..9afda42 100644 --- a/Example/HMSSDKExample/Meeting/PiP/PiPView.swift +++ b/Example/HMSSDKExample/Meeting/PiP/PiPView.swift @@ -16,7 +16,10 @@ struct PiPView: View { var body: some View { if model.pipViewEnabled { VStack { - if let track = model.screenTrack { + if let roomEndReason = model.roomEndedString { + Text(roomEndReason) + } + else if let track = model.screenTrack { GeometryReader { geo in HMSSampleBufferSwiftUIView(track: track, contentMode: .scaleAspectFit, preferredSize: geo.size, model: model) .frame(width: geo.size.width, height: geo.size.height) @@ -27,9 +30,9 @@ struct PiPView: View { } else { Text(model.name ?? "NA") - .foregroundColor(.white) } } + .foregroundColor(.white) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.black) } @@ -67,5 +70,6 @@ public struct HMSSampleBufferSwiftUIView: UIViewRepresentable { public static func dismantleUIView(_ uiView: HMSSampleBufferDisplayView, coordinator: ()) { uiView.isEnabled = false + uiView.track = nil } } diff --git a/Example/HMSSDKExample/Meeting/RolePreviewViewController.swift b/Example/HMSSDKExample/Meeting/RolePreviewViewController.swift index 7301780..83ea682 100644 --- a/Example/HMSSDKExample/Meeting/RolePreviewViewController.swift +++ b/Example/HMSSDKExample/Meeting/RolePreviewViewController.swift @@ -28,8 +28,8 @@ class RolePreviewViewController: PreviewViewController { super.viewDidLoad() interactor.hmsSDK?.preview(role: roleChangeRequest.suggestedRole, completion: { [weak self] tracks, error in - guard error == nil else { - self?.presentAlert(error?.description ?? "") + if let error = error as? HMSError { + self?.presentAlert(error.localizedDescription) return } self?.setupTracks(tracks: tracks ?? []) diff --git a/HMSSDK.podspec b/HMSSDK.podspec index 0355c8c..90d15c3 100644 --- a/HMSSDK.podspec +++ b/HMSSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HMSSDK' - s.version = '0.3.3' + s.version = '0.4.1' s.summary = 'HMS Videoconferencing iOS SDK' s.description = <<-DESC @@ -10,8 +10,8 @@ TODO: Add long description of the pod here. s.homepage = 'https://github.com/100mslive/100ms-ios-sdk/' s.license = { :type => 'MIT'} s.author = { 'Dmitry Fedoseyev' => 'dmitry@100ms.live', 'Yogesh Singh' => 'yogesh@100ms.live', 'Pawan Dixit' => 'pawan@100ms.live'} - s.source = { :http => 'https://github.com/100mslive/100ms-ios-sdk/releases/download/0.3.3/HMSSDK.xcframework.zip', - :sha256 => '6a7df9f1d050caa5ca3bfbb0a66ce09b7e3b840b1ca84363fe2ee73852d53b7d' + s.source = { :http => 'https://github.com/100mslive/100ms-ios-sdk/releases/download/0.4.1/HMSSDK.xcframework.zip', + :sha256 => '1d9783c4d683b4e7bcb0d091fea1e676b04d04f3db182edb84906ee229114251' } s.ios.deployment_target = '12.0' s.vendored_frameworks = 'HMSSDK.xcframework' diff --git a/Package.swift b/Package.swift index 463ff33..a3e03a2 100644 --- a/Package.swift +++ b/Package.swift @@ -14,8 +14,8 @@ let package = Package( targets: [ .binaryTarget( name: "HMSSDK", - url: "https://github.com/100mslive/100ms-ios-sdk/releases/download/0.3.3/HMSSDK.xcframework.zip", - checksum: "6a7df9f1d050caa5ca3bfbb0a66ce09b7e3b840b1ca84363fe2ee73852d53b7d" + url: "https://github.com/100mslive/100ms-ios-sdk/releases/download/0.4.1/HMSSDK.xcframework.zip", + checksum: "1d9783c4d683b4e7bcb0d091fea1e676b04d04f3db182edb84906ee229114251" ), .binaryTarget( name: "WebRTC",