From 323f9f88af5f892f9384e78af52b5c26544e3123 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 00:53:47 +0300 Subject: [PATCH 1/7] Support to get stats periodically and start implementing test code --- .../{ios-build.yml => ios-build-and-test.yml} | 8 +- WebRTC-Sample-App/VideoViewController.swift | 2 + WebRTCiOSSDK.xcodeproj/project.pbxproj | 29 ++- WebRTCiOSSDK/api/AntMediaClient.swift | 69 +++++- WebRTCiOSSDK/api/AntMediaClientDelegate.swift | 9 + WebRTCiOSSDK/api/AntMediaClientProtocol.swift | 14 ++ WebRTCiOSSDK/api/ConferenceClient.swift | 231 ------------------ WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift | 34 ++- 8 files changed, 144 insertions(+), 252 deletions(-) rename .github/workflows/{ios-build.yml => ios-build-and-test.yml} (71%) delete mode 100644 WebRTCiOSSDK/api/ConferenceClient.swift diff --git a/.github/workflows/ios-build.yml b/.github/workflows/ios-build-and-test.yml similarity index 71% rename from .github/workflows/ios-build.yml rename to .github/workflows/ios-build-and-test.yml index 308d787..8224fb7 100644 --- a/.github/workflows/ios-build.yml +++ b/.github/workflows/ios-build-and-test.yml @@ -1,4 +1,4 @@ -name: iOS Build +name: iOS Build and Test on: push @@ -12,9 +12,15 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '15.3' + + - name: Install xcpretty + run: gem install xcpretty - name: Build WebRTCiOSSDK run: xcodebuild -scheme WebRTCiOSSDK -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build + + - name: Test WebRTCiOSSDK + run: xcodebuild -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" ARCHS=arm64 test | xcpretty - name: Build WebRTCSampleApp run: xcodebuild -scheme WebRTC-Sample-App -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build diff --git a/WebRTC-Sample-App/VideoViewController.swift b/WebRTC-Sample-App/VideoViewController.swift index 7ff73ac..c3e9bfd 100644 --- a/WebRTC-Sample-App/VideoViewController.swift +++ b/WebRTC-Sample-App/VideoViewController.swift @@ -289,6 +289,8 @@ extension VideoViewController: AntMediaClientDelegate { if let audioFile = self.audioFileUrl { sendMp3File(url: audioFile); } + + self.client?.registerStatsListener(for: streamId) } func publishFinished(streamId: String) { diff --git a/WebRTCiOSSDK.xcodeproj/project.pbxproj b/WebRTCiOSSDK.xcodeproj/project.pbxproj index 720efa9..26dcd4e 100644 --- a/WebRTCiOSSDK.xcodeproj/project.pbxproj +++ b/WebRTCiOSSDK.xcodeproj/project.pbxproj @@ -15,10 +15,11 @@ A8B966092A069A2C00D67CA1 /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8DABFFD2A063A500007CDE7 /* WebRTC.xcframework */; }; A8B9660A2A069A2C00D67CA1 /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A8DABFFD2A063A500007CDE7 /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A8CAF2BF2C0B1DFC00520691 /* ClientStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CAF2BD2C0B1DFC00520691 /* ClientStatistics.swift */; }; + A8CAF2C02C0B90DD00520691 /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8DABFFD2A063A500007CDE7 /* WebRTC.xcframework */; }; + A8CAF2C12C0B90DD00520691 /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A8DABFFD2A063A500007CDE7 /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A8DABFD32A0639D80007CDE7 /* WebRTCiOSSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8DABFCA2A0639D80007CDE7 /* WebRTCiOSSDK.framework */; }; A8DABFD82A0639D80007CDE7 /* WebRTCiOSSDKTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DABFD72A0639D80007CDE7 /* WebRTCiOSSDKTests.swift */; }; A8DABFD92A0639D80007CDE7 /* WebRTCiOSSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = A8DABFCD2A0639D80007CDE7 /* WebRTCiOSSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A8DABFEF2A063A020007CDE7 /* ConferenceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DABFE32A063A020007CDE7 /* ConferenceClient.swift */; }; A8DABFF02A063A020007CDE7 /* AntMediaClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DABFE42A063A020007CDE7 /* AntMediaClient.swift */; }; A8DABFF12A063A020007CDE7 /* AntMediaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DABFE52A063A020007CDE7 /* AntMediaError.swift */; }; A8DABFF22A063A020007CDE7 /* AntMediaClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8DABFE62A063A020007CDE7 /* AntMediaClientProtocol.swift */; }; @@ -123,6 +124,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + A8CAF2C22C0B90DD00520691 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + A8CAF2C12C0B90DD00520691 /* WebRTC.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; A8DAC04E2A063C4B0007CDE7 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -150,7 +162,6 @@ A8DABFCD2A0639D80007CDE7 /* WebRTCiOSSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebRTCiOSSDK.h; sourceTree = ""; }; A8DABFD22A0639D80007CDE7 /* WebRTCiOSSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebRTCiOSSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A8DABFD72A0639D80007CDE7 /* WebRTCiOSSDKTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTCiOSSDKTests.swift; sourceTree = ""; }; - A8DABFE32A063A020007CDE7 /* ConferenceClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceClient.swift; sourceTree = ""; }; A8DABFE42A063A020007CDE7 /* AntMediaClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AntMediaClient.swift; sourceTree = ""; }; A8DABFE52A063A020007CDE7 /* AntMediaError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AntMediaError.swift; sourceTree = ""; }; A8DABFE62A063A020007CDE7 /* AntMediaClientProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AntMediaClientProtocol.swift; sourceTree = ""; }; @@ -209,6 +220,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A8CAF2C02C0B90DD00520691 /* WebRTC.xcframework in Frameworks */, A8DABFD32A0639D80007CDE7 /* WebRTCiOSSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -307,7 +319,6 @@ A8DABFE22A063A020007CDE7 /* api */ = { isa = PBXGroup; children = ( - A8DABFE32A063A020007CDE7 /* ConferenceClient.swift */, A8DABFE42A063A020007CDE7 /* AntMediaClient.swift */, A8DABFE52A063A020007CDE7 /* AntMediaError.swift */, A8DABFE62A063A020007CDE7 /* AntMediaClientProtocol.swift */, @@ -464,6 +475,7 @@ A8DABFCE2A0639D80007CDE7 /* Sources */, A8DABFCF2A0639D80007CDE7 /* Frameworks */, A8DABFD02A0639D80007CDE7 /* Resources */, + A8CAF2C22C0B90DD00520691 /* Embed Frameworks */, ); buildRules = ( ); @@ -651,7 +663,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A8DABFEF2A063A020007CDE7 /* ConferenceClient.swift in Sources */, A8DABFF92A063A020007CDE7 /* String.swift in Sources */, A8DABFF82A063A020007CDE7 /* Dictionary.swift in Sources */, A8DABFF02A063A020007CDE7 /* AntMediaClient.swift in Sources */, @@ -1010,6 +1021,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 2YK9J8G25K; GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.WebRTCiOSSDKTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1027,6 +1039,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 2YK9J8G25K; GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.WebRTCiOSSDKTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1123,7 +1136,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = MJU7KX4L7S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.webrtc.sample.AppTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1143,7 +1156,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = MJU7KX4L7S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.webrtc.sample.AppTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1162,7 +1175,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = MJU7KX4L7S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.webrtc.sample.AppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1181,7 +1194,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = MJU7KX4L7S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.antmedia.ios.webrtc.sample.AppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/WebRTCiOSSDK/api/AntMediaClient.swift b/WebRTCiOSSDK/api/AntMediaClient.swift index 40d3f4f..c72c934 100644 --- a/WebRTCiOSSDK/api/AntMediaClient.swift +++ b/WebRTCiOSSDK/api/AntMediaClient.swift @@ -73,7 +73,9 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { var streamsInTheRoom:[String] = []; var audioLevelGetterTimer: Timer?; - + + var rtcStatsTimer: Timer? + var rtcStatsStreamIdSet = Set() //private var webRTCClient: WebRTCClient?; private var webRTCClientMap: [String: WebRTCClient] = [:] @@ -474,6 +476,7 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { } else { //removing means that user requests to stop + unregisterStatsListener(streamId: tmpStreamId); self.webRTCClientMap.removeValue(forKey: tmpStreamId)?.disconnect(); if (isWebSocketConnected) { @@ -493,6 +496,7 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { else if (self.playerStreamId == tmpStreamId) { self.playerStreamId = nil; } + } } @@ -987,7 +991,9 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { else if definition == "play_finished" { AntMediaClient.printf("Playing has finished") self.streamsInTheRoom.removeAll(); - self.delegate?.playFinished(streamId: message[STREAM_ID] as! String) + let streamId = message[STREAM_ID] as! String + self.delegate?.playFinished(streamId: streamId) + self.unregisterStatsListener(streamId: streamId) } else if definition == "publish_started" { let streamId = message[STREAM_ID] as! String @@ -999,6 +1005,7 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { let streamId = message[STREAM_ID] as! String AntMediaClient.printf("Publish finished: Let's close") self.delegate?.publishFinished(streamId: streamId) + self.unregisterStatsListener(streamId: streamId) } else if definition == JOINED_ROOM_DEFINITION { @@ -1103,7 +1110,46 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { } } + public func registerStatsListener(for streamId:String, timeInterval:Double = 5) { + self.rtcStatsTimer?.invalidate(); + + self.rtcStatsStreamIdSet.insert(streamId) + self.rtcStatsTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { [weak self] timer in + + guard let self = self else { return } + + var itemsToRemove: Set = [] + + for streamIdInSet in rtcStatsStreamIdSet + { + if let webRTCClient = self.webRTCClientMap[streamIdInSet] + { + webRTCClient.getStats(handler:{ report in + self.delegate?.onStats(streamId: streamIdInSet, statistics: report) + }); + } + else { + itemsToRemove.insert(streamIdInSet); + } + } + + + for itemToRemove in itemsToRemove { + self.unregisterStatsListener(streamId: itemToRemove); + } + } + } + + public func unregisterStatsListener(streamId: String) { + self.rtcStatsStreamIdSet.remove(streamId); + if (self.rtcStatsStreamIdSet.isEmpty) { + self.rtcStatsTimer?.invalidate(); + self.rtcStatsTimer = nil + } + } + public func getStats(completionHandler: @escaping (RTCStatisticsReport) -> Void, streamId:String = "") { + self.webRTCClientMap[self.getStreamId(streamId)]?.getStats(handler: completionHandler) } @@ -1209,6 +1255,8 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { // remove audio level extractor self.removeAudioLevelExtractor() + self.invalidateTimers(); + self.webSocket = nil; } @@ -1232,12 +1280,19 @@ open class AntMediaClient: NSObject, AntMediaClientProtocol { ) } - deinit { + func invalidateTimers() { audioLevelGetterTimer?.invalidate() audioLevelGetterTimer = nil pingTimer?.invalidate() pingTimer = nil + + rtcStatsTimer?.invalidate() + rtcStatsTimer = nil + } + + deinit { + invalidateTimers(); } } @@ -1323,8 +1378,6 @@ extension AntMediaClient: WebRTCClientDelegate { } } - - } extension AntMediaClient: WebSocketDelegate { @@ -1345,8 +1398,12 @@ extension AntMediaClient: WebSocketDelegate { self.websocketConnected() self.delegate?.clientDidConnect(self) + + + //too keep the connetion alive send ping command for every 10 seconds - pingTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { pingTimer in + pingTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] pingTimer in + guard let self = self else { return } let jsonString = self.getPingMessage().json self.webSocket?.write(string: jsonString) } diff --git a/WebRTCiOSSDK/api/AntMediaClientDelegate.swift b/WebRTCiOSSDK/api/AntMediaClientDelegate.swift index 8ca8af4..1879a17 100644 --- a/WebRTCiOSSDK/api/AntMediaClientDelegate.swift +++ b/WebRTCiOSSDK/api/AntMediaClientDelegate.swift @@ -147,6 +147,11 @@ public protocol AntMediaClientDelegate: AnyObject { */ func onLoadBroadcastObject(streamId: String, message: [String: Any]) + /** + It's called after`registerStatsListener`is `AntMediaClient` is called + */ + func onStats(streamId:String, statistics:RTCStatisticsReport); + /** It's called after join to the room. - streamId: the id of the stream tha can be used to publish stream. @@ -270,5 +275,9 @@ public extension AntMediaClientDelegate { AntMediaClient.printf(payload?.json ?? "") } + func onStats(streamId:String, statistics:RTCStatisticsReport) { + AntMediaClient.printf("streamId: \(streamId) stats received") + } + } diff --git a/WebRTCiOSSDK/api/AntMediaClientProtocol.swift b/WebRTCiOSSDK/api/AntMediaClientProtocol.swift index 063b5a7..5aa3dfe 100644 --- a/WebRTCiOSSDK/api/AntMediaClientProtocol.swift +++ b/WebRTCiOSSDK/api/AntMediaClientProtocol.swift @@ -280,6 +280,20 @@ public protocol AntMediaClientProtocol { */ func getStats(completionHandler: @escaping (RTCStatisticsReport) -> Void, streamId:String); + /** + Register a stats listener to get the stats peridocially. This is a helper method to make developers life easy. + Under the hood, it agains calls the `getStats` method. After you register for stats, `onStats` method of `AntMediaClientDelegate` is being called periodically + */ + func registerStatsListener(for streamId:String, timeInterval:Double); + + + /** + Unregister stat listener for a specific streamId. After you call this method,`onStats` method of `AntMediaClientDelegate` will not be called for this streamId again. + - Parameters: + - streamId: is the stream id to be removed from listening stats + */ + func unregisterStatsListener(streamId: String) + /** Set the max video bitrate for publishing the stream */ diff --git a/WebRTCiOSSDK/api/ConferenceClient.swift b/WebRTCiOSSDK/api/ConferenceClient.swift deleted file mode 100644 index 279fc1c..0000000 --- a/WebRTCiOSSDK/api/ConferenceClient.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// ConferenceClient.swift -// WebRTCiOSSDK -// -// Created by mekya on 12.08.2020. -// Copyright © 2020 AntMedia. All rights reserved. -// - -import Foundation -import Starscream -import WebRTC - -public protocol ConferenceClientProtocol { - - /** - Join the room - - roomId: the id of the room that conference client joins - - streamId: the preferred stream id that can be sent to the server for publishing. Server likely responds the same streamId in - delegate's streamIdToPublish method - */ - func joinRoom(roomId:String, streamId:String) - - /* - Leave the room - */ - func leaveRoom(); -} - -public protocol ConferenceClientDelegate: AnyObject -{ - /** - It's called after join to the room. - - streamId: the id of the stream tha can be used to publish stream. - It's not an obligation to publish a stream. It changes according to the project - */ - func streamIdToPublish(streamId: String); - - /** - Called when new streams join to the room. So that they can be played - - streams: stream id array of the streams that join to the room - */ - func newStreamsJoined(streams: [String]); - - /** - Called when some streams leaves from the room. So that players can be removed from the user interface - - streams: stream id array of the stream that leaves from the room - */ - func streamsLeft(streams: [String]); -} - -@available(*, deprecated, message: "Use AntMediaClient directly and take a look at the sample") -open class ConferenceClient: ConferenceClientProtocol, WebSocketDelegate -{ - var serverURL: String; - var webSocket: WebSocket; - var roomId: String!; - var streamId: String?; - var streamsInTheRoom:[String] = []; - - weak var delegate: ConferenceClientDelegate?; - - var roomInfoGetterTimer: Timer?; - - public init(serverURL:String, conferenceClientDelegate:ConferenceClientDelegate) - { - self.serverURL = serverURL; - var request = URLRequest(url: URL(string: self.serverURL)!) - request.timeoutInterval = 5 - webSocket = WebSocket(request: request) - webSocket.delegate = self; - self.delegate = conferenceClientDelegate; - } - - deinit { - roomInfoGetterTimer?.invalidate() - } - - public func didConnected(socket: WebSocketClient) - { - let joinRoomMessage = [ - COMMAND: "joinRoom", - ROOM_ID: self.roomId!, - MODE: "multitrack", - STREAM_ID: self.streamId ?? "" ] as [String : Any] - - webSocket.write(string: joinRoomMessage.json) - } - - - - public func receiveMessage(socket: WebSocketClient, text: String) { - - // AntMediaClient.printf("Received message \(text)") - if let message = text.toJSON() - { - - guard let command = message[COMMAND] as? String else { - return - } - - switch command { - case NOTIFICATION: - guard let definition = message[DEFINITION] as? String else { - return - } - if definition == JOINED_ROOM_DEFINITION - { - if let streamId = message[STREAM_ID] as? String { - self.streamId = streamId - self.delegate?.streamIdToPublish(streamId: streamId); - } - - if let streams = message[STREAMS] as? [String] { - self.streamsInTheRoom = streams; - if (self.streamsInTheRoom.count > 0) { - self.delegate?.newStreamsJoined(streams: streams); - } - } - - //start periodic check - roomInfoGetterTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { pingTimer in - let jsonString = - [ COMMAND: "getRoomInfo", - ROOM_ID: self.roomId as String, - STREAM_ID: self.streamId ?? "" - ] as [String: Any] - - self.webSocket.write(string: jsonString.json) - } - - } - break; - case ROOM_INFORMATION_COMMAND: - if let updatedStreamsInTheRoom = message[STREAMS] as? [String] { - //check that there is a new stream exists - var newStreams:[String] = [] - var leavedStreams: [String] = [] - for stream in updatedStreamsInTheRoom - { - // AntMedia.printf("stream in updatestreamInTheRoom \(stream)") - if (!self.streamsInTheRoom.contains(stream)) { - newStreams.append(stream) - } - } - //check that any stream is leaved - for stream in self.streamsInTheRoom { - if (!updatedStreamsInTheRoom.contains(stream)) { - leavedStreams.append(stream) - } - } - - self.streamsInTheRoom = updatedStreamsInTheRoom - - if (newStreams.count > 0) { - self.delegate?.newStreamsJoined(streams: newStreams) - } - - if (leavedStreams.count > 0) { - self.delegate?.streamsLeft(streams: leavedStreams) - } - - } - - break; - - default: - print("default case") - - } - - - - } else { - print("WebSocket message JSON parsing error: " + text) - } - } - - - - public func joinRoom(roomId: String, streamId:String) { - self.roomId = roomId; - self.streamId = streamId; - webSocket.connect() - } - - public func leaveRoom() { - roomInfoGetterTimer?.invalidate() - let joinRoomMessage = [ - COMMAND: "leaveRoom", - ROOM_ID: self.roomId!, - STREAM_ID: self.streamId ?? "" ] as [String : Any] - - webSocket.write(string: joinRoomMessage.json) - } - - public func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocketClient) { - switch event { - case .connected(let headers): - AntMediaClient.printf("websocket is connected: \(headers)") - didConnected(socket: client); - break; - case .disconnected(let reason, let code): - AntMediaClient.printf("websocket is disconnected: \(reason) with code: \(code)"); - break; - case .text(let string): - receiveMessage(socket: client, text: string) - break; - case .binary(let data): - print("Received data: \(data.count)") - break; - case .ping(_): - break - case .pong(_): - break - case .viabilityChanged(_): - break - case .reconnectSuggested(_): - break - case .cancelled: - break; - case .error(let error): - AntMediaClient.printf("Error occured on websocket connection \(String(describing: error))"); - break; - default: - AntMediaClient.printf("Unexpected command received from websocket"); - break; - } - } - - -} diff --git a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift index 39727d7..1765937 100644 --- a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift +++ b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift @@ -18,19 +18,41 @@ final class WebRTCiOSSDKTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testExample() throws { + func testRegisterStatsListener() async throws { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. // Any test you write for XCTest can be annotated as throws and async. // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } + let client = AntMediaClient.init(); + XCTAssertNotNil(client); + XCTAssertNil(client.rtcStatsTimer); + + client.registerStatsListener(for: "stream1", timeInterval:1) + + XCTAssertNotNil(client.rtcStatsTimer); + XCTAssertTrue(client.rtcStatsStreamIdSet.contains("stream1")) + + RunLoop.current.run(until: Date().addingTimeInterval(2)) - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. + let rtcStatsTimerExpectation = expectation(description: "rtcStatsTimer Invalidated") + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + if (client.rtcStatsTimer == nil && client.rtcStatsStreamIdSet.isEmpty) { + rtcStatsTimerExpectation.fulfill() + } } + + await fulfillment(of: [rtcStatsTimerExpectation], timeout: 3) + + + } + //func testPerformanceExample() throws { + // This is an example of a performance test case. + // self.measure { + // } + //} + } From 6a21b77a5c0bca6e09376fc87fd6814dc6260e91 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:05:03 +0300 Subject: [PATCH 2/7] Add WebRTCiOSSDK and WebRTCiOSSDKTests schemes --- .../xcschemes/WebRTCiOSSDK.xcscheme | 80 +++++++++++++++++++ .../xcschemes/WebRTCiOSSDKTests.xcscheme | 74 +++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDK.xcscheme create mode 100644 WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDKTests.xcscheme diff --git a/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDK.xcscheme b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDK.xcscheme new file mode 100644 index 0000000..42be565 --- /dev/null +++ b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDK.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDKTests.xcscheme b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDKTests.xcscheme new file mode 100644 index 0000000..be8df57 --- /dev/null +++ b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTCiOSSDKTests.xcscheme @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1bdb9d3adf6d623d593562a21d71616269aceb88 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:06:06 +0300 Subject: [PATCH 3/7] Remove xcpretty --- .github/workflows/ios-build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios-build-and-test.yml b/.github/workflows/ios-build-and-test.yml index 8224fb7..a1ab90e 100644 --- a/.github/workflows/ios-build-and-test.yml +++ b/.github/workflows/ios-build-and-test.yml @@ -20,7 +20,7 @@ jobs: run: xcodebuild -scheme WebRTCiOSSDK -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build - name: Test WebRTCiOSSDK - run: xcodebuild -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" ARCHS=arm64 test | xcpretty + run: xcodebuild -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" ARCHS=arm64 test - name: Build WebRTCSampleApp run: xcodebuild -scheme WebRTC-Sample-App -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build From 19793e2bb325cb2ed93098b923391f436edb1bc4 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:20:34 +0300 Subject: [PATCH 4/7] clean build & remove ARCHS parameter --- .github/workflows/ios-build-and-test.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ios-build-and-test.yml b/.github/workflows/ios-build-and-test.yml index a1ab90e..0720f58 100644 --- a/.github/workflows/ios-build-and-test.yml +++ b/.github/workflows/ios-build-and-test.yml @@ -12,15 +12,16 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '15.3' - - - name: Install xcpretty - run: gem install xcpretty + + - name: Install simulator + run: xcversion simulators --install='iOS 14.3' + - name: Build WebRTCiOSSDK run: xcodebuild -scheme WebRTCiOSSDK -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build - name: Test WebRTCiOSSDK - run: xcodebuild -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" ARCHS=arm64 test + run: xcodebuild clean -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" test - name: Build WebRTCSampleApp run: xcodebuild -scheme WebRTC-Sample-App -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build From 9f9d0b9afd5214f9f49f20cff93165ebad69532f Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:23:47 +0300 Subject: [PATCH 5/7] Remove simulator installation --- .github/workflows/ios-build-and-test.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ios-build-and-test.yml b/.github/workflows/ios-build-and-test.yml index 0720f58..7e22e6f 100644 --- a/.github/workflows/ios-build-and-test.yml +++ b/.github/workflows/ios-build-and-test.yml @@ -12,16 +12,12 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '15.3' - - - name: Install simulator - run: xcversion simulators --install='iOS 14.3' - - name: Build WebRTCiOSSDK run: xcodebuild -scheme WebRTCiOSSDK -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build - name: Test WebRTCiOSSDK - run: xcodebuild clean -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" test + run: xcodebuild clean -scheme WebRTCiOSSDK -destination "platform=iOS Simulator,name=iPhone 15" test - name: Build WebRTCSampleApp run: xcodebuild -scheme WebRTC-Sample-App -configuration Release -destination "generic/platform=iOS" ARCHS=arm64 CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build From 066ce70e80ab2438632130058fd204194cfb0e8b Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:28:50 +0300 Subject: [PATCH 6/7] add WebRTC-Sample-App scheme and fail test on purpose --- .../xcschemes/WebRTC-Sample-App.xcscheme | 78 +++++++++++++++++++ WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift | 3 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTC-Sample-App.xcscheme diff --git a/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTC-Sample-App.xcscheme b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTC-Sample-App.xcscheme new file mode 100644 index 0000000..9c69c5a --- /dev/null +++ b/WebRTCiOSSDK.xcodeproj/xcshareddata/xcschemes/WebRTC-Sample-App.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift index 1765937..8a32afa 100644 --- a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift +++ b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift @@ -42,9 +42,10 @@ final class WebRTCiOSSDKTests: XCTestCase { rtcStatsTimerExpectation.fulfill() } } - + await fulfillment(of: [rtcStatsTimerExpectation], timeout: 3) + XCTAssertTrue(false) } From d3d1d71917911bcefd0d1fb1b9d004b5367b1275 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 2 Jun 2024 01:34:41 +0300 Subject: [PATCH 7/7] fix test case --- WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift index 8a32afa..4127917 100644 --- a/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift +++ b/WebRTCiOSSDKTests/WebRTCiOSSDKTests.swift @@ -44,9 +44,6 @@ final class WebRTCiOSSDKTests: XCTestCase { } await fulfillment(of: [rtcStatsTimerExpectation], timeout: 3) - - XCTAssertTrue(false) - }