From a5bb0e21616954a7d1f0f37ec4784fd2987d41e1 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Tue, 16 Jul 2024 17:27:02 -0600 Subject: [PATCH 01/19] maxBitrate UI --- .../Interactive Viewer/Localizable.strings | 6 +- .../RTSViewer.xcdatamodel/contents | 3 +- .../Persistence/StreamDataManager.swift | 1 + .../Managers/VideoTracksManager.swift | 114 +++++++++--------- .../Models/SavedStreamDetail.swift | 42 ++++--- .../RecentStreams/RecentStreamCell.swift | 10 +- .../StreamDetailInputScreen.swift | 56 ++++++--- .../StreamDetailInputViewModel.swift | 2 + .../RTSViewer.xcodeproj/project.pbxproj | 4 + 9 files changed, 138 insertions(+), 100 deletions(-) diff --git a/interactive-player/Interactive Viewer/Localizable.strings b/interactive-player/Interactive Viewer/Localizable.strings index f4b655f2..cec9f110 100644 --- a/interactive-player/Interactive Viewer/Localizable.strings +++ b/interactive-player/Interactive Viewer/Localizable.strings @@ -25,6 +25,7 @@ "stream-detail-input.minimum-playout-delay-placeholder-label" = "Minimum Playout Delay"; "stream-detail-input.maximum-playout-delay-placeholder-label" = "Maximum Playout Delay"; "stream-detail-input.primary-video-quality-label" = "Primary video quality:"; +"stream-detail-input.max-bitrate-label" = "Maxium Bitrate:"; "stream-detail-server-url-label" = "Server URL"; /** App configuration Screen */ @@ -42,8 +43,9 @@ "recent-streams.account-id.title.label" = "ID:"; "recent-streams.server-url.label" = "Server URL:"; "recent-streams.video-jitter-buffer.label" = "Video Jitter buffer in ms:"; -"recent-streams.min-playout-delay.label" = "Min Playout delay:"; -"recent-streams.max-playout-delay.label" = "Max Playout delay:"; +"recent-streams.min-playout-delay.label" = "Min Playout Delay:"; +"recent-streams.max-playout-delay.label" = "Max Playout Delay:"; +"recent-streams.max-bitrate.label" = "Maxium Bitrate:"; "recent-streams.disable-audio.label" = "Disable Audio:"; "recent-streams.primary-video-quality.label" = "Primary video quality:"; "recent-streams.save-logs.label" = "Save Logs:"; diff --git a/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents b/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents index 6c955cde..fad55309 100644 --- a/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents +++ b/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents @@ -1,9 +1,10 @@ - + + diff --git a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift index 8c351dea..9c849488 100644 --- a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift +++ b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift @@ -139,6 +139,7 @@ final class StreamDataManager: NSObject, StreamDataManagerProtocol { streamDetailToSave.maxPlayoutDelay = streamDetail.maxPlayoutDelay.map { NSNumber(value: $0) } streamDetailToSave.disableAudio = streamDetail.disableAudio streamDetailToSave.primaryVideoQuality = streamDetail.primaryVideoQuality.rawValue + streamDetailToSave.maxBitrate = Int32(streamDetail.maxBitrate) streamDetailToSave.saveLogs = streamDetail.saveLogs // Delete streams that are older and exceeding the maximum allowed count diff --git a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift index 0128eeaf..48c0ef47 100644 --- a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift +++ b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift @@ -2,14 +2,13 @@ // VideoTracksManager.swift // -import Foundation import Combine -import RTSCore +import Foundation import MillicastSDK import os +import RTSCore final actor VideoTracksManager { - typealias ViewID = String private static let logger = Logger( @@ -36,13 +35,14 @@ final actor VideoTracksManager { private var sourceToSimulcastLayersMapping: [SourceID: [MCRTSRemoteTrackLayer]] = [:] private var sourceToSelectedVideoQualityAndLayerMapping: [SourceID: VideoQualityAndLayerPair] = [:] { didSet { - let sourceToVideoQuality = sourceToSelectedVideoQualityAndLayerMapping - .mapValues({ + let sourceToVideoQuality = self.sourceToSelectedVideoQualityAndLayerMapping + .mapValues { $0.videoQuality - }) - videoQualitySubject.send(sourceToVideoQuality) + } + self.videoQualitySubject.send(sourceToVideoQuality) } } + private var layerEventsObservationDictionary: [SourceID: Task] = [:] private var sourceToTasks: [SourceID: SerialTasks] = [:] private let videoQualitySubject: CurrentValueSubject<[SourceID: VideoQuality], Never> = CurrentValueSubject([:]) @@ -56,9 +56,8 @@ final actor VideoTracksManager { func observeLayerUpdates(for source: StreamSource) { Task { [weak self] in - guard let self, await self.layerEventsObservationDictionary[source.sourceId] == nil else { - return - } + guard let self, + await self.layerEventsObservationDictionary[source.sourceId] == nil else { return } let layerEventsObservationTask = Task { for await layerEvent in source.videoTrack.layers() { let simulcastLayers = layerEvent.layers() @@ -73,12 +72,12 @@ final actor VideoTracksManager { } func reset() { - sourceToTasks.removeAll() - layerEventsObservationDictionary.removeAll() - sourceToActiveViewsMapping.removeAll() - viewToRequestedVideoQualityMapping.removeAll() - sourceToSimulcastLayersMapping.removeAll() - sourceToSelectedVideoQualityAndLayerMapping.removeAll() + self.sourceToTasks.removeAll() + self.layerEventsObservationDictionary.removeAll() + self.sourceToActiveViewsMapping.removeAll() + self.viewToRequestedVideoQualityMapping.removeAll() + self.sourceToSimulcastLayersMapping.removeAll() + self.sourceToSelectedVideoQualityAndLayerMapping.removeAll() } func enableTrack(for source: StreamSource, with preferredVideoQuality: VideoQuality, on view: ViewID) async { @@ -86,19 +85,19 @@ final actor VideoTracksManager { Self.logger.debug("♼ Request to enable video track for source \(sourceId) with preferredVideoQuality \(preferredVideoQuality.displayText) from view \(view.description)") // If the view has already requested the same video quality before? if yes, exit - guard viewToRequestedVideoQualityMapping[view] != preferredVideoQuality else { + guard self.viewToRequestedVideoQualityMapping[view] != preferredVideoQuality else { Self.logger.debug("♼ Exiting - View already presented for source \(sourceId) with preferredVideoQuality \(preferredVideoQuality.displayText)") return } - let activeViewsForSource = sourceToActiveViewsMapping[sourceId] ?? [] + let activeViewsForSource = self.sourceToActiveViewsMapping[sourceId] ?? [] // Calculate the video quality to project from the requested list // Note: Only one layer can be selected for a source at a given time var videoQualitiesRequestedForSource = activeViewsForSource - .compactMap { viewToRequestedVideoQualityMapping[$0] } + .compactMap { self.viewToRequestedVideoQualityMapping[$0] } videoQualitiesRequestedForSource.append(preferredVideoQuality) let bestVideoQualityFromTheList = videoQualitiesRequestedForSource.bestVideoQualityFromTheRequestedList - let simulcastLayers = sourceToSimulcastLayersMapping[sourceId] + let simulcastLayers = self.sourceToSimulcastLayersMapping[sourceId] let layerToSelect = simulcastLayers.map { $0.matching(quality: bestVideoQualityFromTheList) } ?? nil Self.logger.debug("♼ Source \(sourceId) has \(simulcastLayers?.count ?? 0) simulcast layers") @@ -107,29 +106,29 @@ final actor VideoTracksManager { Self.logger.debug("♼ Add active view \(view.description) for source \(sourceId)") // Update view's requested video quality - viewToRequestedVideoQualityMapping[view] = preferredVideoQuality + self.viewToRequestedVideoQualityMapping[view] = preferredVideoQuality // Add new view to the list of active views for that source if var views = sourceToActiveViewsMapping[sourceId] { views.append(view) - sourceToActiveViewsMapping[source.sourceId] = views + self.sourceToActiveViewsMapping[source.sourceId] = views } else { - sourceToActiveViewsMapping[source.sourceId] = [view] + self.sourceToActiveViewsMapping[source.sourceId] = [view] } do { if let layerToSelect { Self.logger.debug("♼ Simulcast layer - \(layerToSelect) for source \(sourceId)") Self.logger.debug("♼ Selecting videoquality \(videoQualityToSelect.displayText) for source \(sourceId) on view \(view)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) + try await self.queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) } else { Self.logger.debug("♼ No simulcast layer for source \(sourceId) matching \(bestVideoQualityFromTheList.displayText)") Self.logger.debug("♼ Selecting videoquality 'Auto' for source \(sourceId) on view \(view)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source) + try await self.queueEnableTrack(for: source) } } catch { Self.logger.debug("♼🛑 Enabling video track threw error \(error.localizedDescription)") @@ -141,20 +140,22 @@ final actor VideoTracksManager { Self.logger.debug("♼ Request to disable video track for source \(sourceId) on view \(view.description)") Self.logger.debug("♼ Remove view \(view.description) for source \(sourceId)") // Remove view from the list of active views for that source - if var activeViews = sourceToActiveViewsMapping[sourceId], activeViews.contains(where: { $0 == view }) { + if var activeViews = sourceToActiveViewsMapping[sourceId], + activeViews.contains(where: { $0 == view }) + { activeViews.removeAll(where: { $0 == view }) - sourceToActiveViewsMapping[source.sourceId] = !activeViews.isEmpty ? activeViews : nil + self.sourceToActiveViewsMapping[source.sourceId] = !activeViews.isEmpty ? activeViews : nil } // Remove view from View to requested video quality mapping - viewToRequestedVideoQualityMapping[view] = nil + self.viewToRequestedVideoQualityMapping[view] = nil if let activeViews = sourceToActiveViewsMapping[sourceId] { // Calculate the video quality to project from the requested list // Note: Only one projection can exist for a source at a given time - let videoQualitiesRequestedForSource = activeViews.compactMap { viewToRequestedVideoQualityMapping[$0] } + let videoQualitiesRequestedForSource = activeViews.compactMap { self.viewToRequestedVideoQualityMapping[$0] } let bestVideoQualityFromRequested = videoQualitiesRequestedForSource.bestVideoQualityFromTheRequestedList - let simulcastLayers = sourceToSimulcastLayersMapping[sourceId] + let simulcastLayers = self.sourceToSimulcastLayersMapping[sourceId] let layerToSelect = simulcastLayers.map { $0.matching(quality: bestVideoQualityFromRequested) } ?? nil let selectedVideoQuality: VideoQuality = layerToSelect == nil ? .auto : bestVideoQualityFromRequested @@ -164,13 +165,13 @@ final actor VideoTracksManager { if let layerToSelect { Self.logger.debug("♼ Has simulcast layer - \(layerToSelect) for source \(sourceId)") Self.logger.debug("♼ Selecting videoquality \(selectedVideoQuality.displayText) for source \(sourceId); active view \(activeViews)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + try await self.queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) } else { Self.logger.debug("♼ No simulcast layer for source \(sourceId) matching \(bestVideoQualityFromRequested.displayText)") Self.logger.debug("♼ Selecting videoquality 'Auto' for source \(sourceId); active view \(activeViews)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source) + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + try await self.queueEnableTrack(for: source) } } catch { Self.logger.debug("♼🛑 Enabling video track threw error \(error.localizedDescription)") @@ -180,11 +181,11 @@ final actor VideoTracksManager { Self.logger.debug("♼ Disable video track for source \(sourceId) as there are no active views") // Remove selected Video quality for source - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = nil + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = nil removeAllStoredData(for: source) - try await queueDisableTrack(for: source) + try await self.queueDisableTrack(for: source) } catch { Self.logger.debug("♼🛑 Disabling video track threw error \(error.localizedDescription)") } @@ -192,12 +193,13 @@ final actor VideoTracksManager { } func queueEnableTrack(for source: StreamSource, layer: MCRTSRemoteVideoTrackLayer? = nil) async throws { - if sourceToTasks[source.sourceId] == nil { - sourceToTasks[source.sourceId] = SerialTasks() + if self.sourceToTasks[source.sourceId] == nil { + self.sourceToTasks[source.sourceId] = SerialTasks() } - let renderer = rendererRegistry.sampleBufferRenderer(for: source).underlyingRenderer - guard let serialTasks = sourceToTasks[source.sourceId], source.videoTrack.isActive else { return } + let renderer = self.rendererRegistry.sampleBufferRenderer(for: source).underlyingRenderer + guard let serialTasks = sourceToTasks[source.sourceId], + source.videoTrack.isActive else { return } try await serialTasks.enqueue { Self.logger.debug("♼ Queue: Enabling track for source \(source.sourceId) on renderer \(ObjectIdentifier(renderer).debugDescription)") guard !Task.isCancelled, source.videoTrack.isActive else { return } @@ -225,12 +227,14 @@ final actor VideoTracksManager { private extension VideoTracksManager { func addSimulcastLayers(_ layers: [MCRTSRemoteTrackLayer], for source: StreamSource) async { - sourceToSimulcastLayersMapping[source.sourceId] = layers + self.sourceToSimulcastLayersMapping[source.sourceId] = layers Self.logger.debug("♼ Add layers \(layers.count) for \(source.sourceId)") let sourceId = source.sourceId // Choose any active view to reenable the track - guard let activeViews = sourceToActiveViewsMapping[sourceId], let anyActiveView = activeViews.first else { + guard let activeViews = sourceToActiveViewsMapping[sourceId], + let anyActiveView = activeViews.first + else { Self.logger.debug("♼ No active views for \(source.sourceId)") return } @@ -238,14 +242,14 @@ private extension VideoTracksManager { // Calculate the video quality to project from the requested list // Note: Only one projection can exist for a source at a given time let videoQualitiesRequestedForSource = activeViews - .compactMap { viewToRequestedVideoQualityMapping[$0] } + .compactMap { self.viewToRequestedVideoQualityMapping[$0] } let bestVideoQualityFromRequested = videoQualitiesRequestedForSource.bestVideoQualityFromTheRequestedList let layerToSelect = layers.matching(quality: bestVideoQualityFromRequested) let selectedVideoQuality: VideoQuality = layerToSelect == nil ? .auto : bestVideoQualityFromRequested let newVideoQualityAndLayerPair = VideoQualityAndLayerPair(videoQuality: selectedVideoQuality, layer: layerToSelect) - let currentVideoQualityAndLayerPair = sourceToSelectedVideoQualityAndLayerMapping[source.sourceId] + let currentVideoQualityAndLayerPair = self.sourceToSelectedVideoQualityAndLayerMapping[source.sourceId] guard newVideoQualityAndLayerPair != currentVideoQualityAndLayerPair else { // Currently selected video quality matches the newly calculated one, no action needed Self.logger.debug("♼ Exiting - Currently selected videoquality \(selectedVideoQuality.displayText) for source \(sourceId) is already up to date") @@ -256,13 +260,13 @@ private extension VideoTracksManager { if let layerToSelect { Self.logger.debug("♼ Has simulcast layer - \(layerToSelect) for source \(sourceId)") Self.logger.debug("♼ Selecting videoquality \(selectedVideoQuality.displayText) for source \(sourceId) on view \(anyActiveView)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + try await self.queueEnableTrack(for: source, layer: MCRTSRemoteVideoTrackLayer(layer: layerToSelect)) } else { Self.logger.debug("♼ No simulcast layer for source \(sourceId) matching \(bestVideoQualityFromRequested.displayText)") Self.logger.debug("♼ Selecting videoquality 'Auto' for source \(sourceId) on view \(anyActiveView)") - sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair - try await queueEnableTrack(for: source) + self.sourceToSelectedVideoQualityAndLayerMapping[sourceId] = newVideoQualityAndLayerPair + try await self.queueEnableTrack(for: source) } } catch { Self.logger.debug("♼🛑 Enabling video track threw error \(error.localizedDescription)") @@ -271,11 +275,11 @@ private extension VideoTracksManager { func removeAllStoredData(for source: StreamSource) { let sourceId = source.sourceId - let activeViews = sourceToActiveViewsMapping[sourceId] + let activeViews = self.sourceToActiveViewsMapping[sourceId] - activeViews?.forEach { viewToRequestedVideoQualityMapping[$0] = nil } - sourceToSimulcastLayersMapping[sourceId] = nil - sourceToActiveViewsMapping[sourceId] = nil + activeViews?.forEach { self.viewToRequestedVideoQualityMapping[$0] = nil } + self.sourceToSimulcastLayersMapping[sourceId] = nil + self.sourceToActiveViewsMapping[sourceId] = nil } } @@ -283,7 +287,7 @@ private extension VideoTracksManager { private extension VideoTracksManager { func addLayerEventsObservationTask(_ task: Task, for source: StreamSource) { - layerEventsObservationDictionary[source.sourceId] = task + self.layerEventsObservationDictionary[source.sourceId] = task } } diff --git a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift index 5c758cc1..ed673e0b 100644 --- a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift +++ b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift @@ -15,20 +15,20 @@ struct SavedStreamDetail: Identifiable, Equatable { let maxPlayoutDelay: UInt? let disableAudio: Bool let primaryVideoQuality: VideoQuality + let maxBitrate: UInt let saveLogs: Bool - init( - accountID: String, - streamName: String, - subscribeAPI: String, - videoJitterMinimumDelayInMs: UInt, - minPlayoutDelay: UInt?, - maxPlayoutDelay: UInt?, - disableAudio: Bool, - primaryVideoQuality: VideoQuality, - saveLogs: Bool, - dateProvider: DateProvider = DefaultDateProvider() - ) { + init(accountID: String, + streamName: String, + subscribeAPI: String, + videoJitterMinimumDelayInMs: UInt, + minPlayoutDelay: UInt?, + maxPlayoutDelay: UInt?, + disableAudio: Bool, + primaryVideoQuality: VideoQuality, + maxBitrate: UInt, + saveLogs: Bool, + dateProvider: DateProvider = DefaultDateProvider()) { self.id = UUID() self.accountID = accountID self.streamName = streamName @@ -39,19 +39,19 @@ struct SavedStreamDetail: Identifiable, Equatable { self.maxPlayoutDelay = maxPlayoutDelay self.disableAudio = disableAudio self.primaryVideoQuality = primaryVideoQuality + self.maxBitrate = maxBitrate self.saveLogs = saveLogs } } extension SavedStreamDetail { init?(managedObject: StreamDetailManagedObject) { - guard - let accountID = managedObject.accountID, - let streamName = managedObject.streamName, - let lastUsedDate = managedObject.lastUsedDate, - let storedVideoQuality = managedObject.primaryVideoQuality, - let subscribeAPI = managedObject.subscribeAPI, - let primaryVideoQuality = VideoQuality(rawValue: storedVideoQuality) + guard let accountID = managedObject.accountID, + let streamName = managedObject.streamName, + let lastUsedDate = managedObject.lastUsedDate, + let storedVideoQuality = managedObject.primaryVideoQuality, + let subscribeAPI = managedObject.subscribeAPI, + let primaryVideoQuality = VideoQuality(rawValue: storedVideoQuality) else { return nil } @@ -59,6 +59,7 @@ extension SavedStreamDetail { let maxPlayoutDelay = managedObject.maxPlayoutDelay let disableAudio = managedObject.disableAudio let saveLogs = managedObject.saveLogs + let maxBitrate = managedObject.maxBitrate self.id = UUID() self.accountID = accountID @@ -70,6 +71,7 @@ extension SavedStreamDetail { self.maxPlayoutDelay = maxPlayoutDelay.map { UInt(truncating: $0) } self.disableAudio = disableAudio self.primaryVideoQuality = primaryVideoQuality + self.maxBitrate = UInt(maxBitrate) self.saveLogs = saveLogs } - } +} diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift index cb258bb5..18e4b006 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift @@ -25,6 +25,7 @@ struct RecentStreamCell: View { (String(localized: "recent-streams.max-playout-delay.label"), streamDetail.maxPlayoutDelay.map { String($0) } ?? "N/A"), (String(localized: "recent-streams.disable-audio.label"), String(streamDetail.disableAudio)), (String(localized: "recent-streams.primary-video-quality.label"), streamDetail.primaryVideoQuality.displayText), + (String(localized: "recent-streams.max-bitrate.label"), String(streamDetail.maxBitrate)), (String(localized: "recent-streams.save-logs.label"), String(streamDetail.saveLogs)) ] ) @@ -34,10 +35,9 @@ struct RecentStreamCell: View { private let action: () -> Void - init( - streamDetail: SavedStreamDetail, - action: @escaping () -> Void - ) { + init(streamDetail: SavedStreamDetail, + action: @escaping () -> Void) + { self.streamDetail = streamDetail self.action = action } @@ -78,7 +78,6 @@ struct RecentStreamCell: View { private extension Font { static let streamDetailFont: Font = .custom("AvenirNext-Regular", size: FontSize.subhead, relativeTo: .subheadline) - } struct RecentStreamCell_Previews: PreviewProvider { @@ -93,6 +92,7 @@ struct RecentStreamCell_Previews: PreviewProvider { maxPlayoutDelay: 0, disableAudio: true, primaryVideoQuality: .auto, + maxBitrate: 0, saveLogs: false ), action: {} diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 0f34903a..244b1c85 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -8,11 +8,11 @@ import SwiftUI // swiftlint:disable type_body_length struct StreamDetailInputScreen: View { - enum InputFocusable: Hashable { - case accountID - case streamName + case accountID + case streamName } + @Binding private var streamingScreenContext: StreamingView.Context? @State private var streamName: String = "" @@ -22,12 +22,13 @@ struct StreamDetailInputScreen: View { @State private var subscribeAPI: String = SubscriptionConfiguration.Constants.developmentSubscribeURL @State private var disableAudio: Bool = false @State private var saveLogs: Bool = false - @State private var jitterBufferDelayInMs: Float = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var jitterBufferDelayInMs: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @State private var primaryVideoQuality: VideoQuality = .auto + @State private var maxBitrate: UInt = 0 @State private var isShowingSettingsView: Bool = false @State private var showPlayoutDelay: Bool = false - @State private var minPlayoutDelay: Float = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) - @State private var maxPlayoutDelay: Float = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var minPlayoutDelay: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var maxPlayoutDelay: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @FocusState private var inputFocus: InputFocusable? @@ -128,6 +129,7 @@ struct StreamDetailInputScreen: View { maxPlayoutDelay: maxPlayoutDelay, disableAudio: disableAudio, primaryVideoQuality: primaryVideoQuality, + maxBitrate: maxBitrate, saveLogs: saveLogs, persistStream: true ) @@ -273,7 +275,7 @@ struct StreamDetailInputScreen: View { ) Slider( value: $jitterBufferDelayInMs, - in: (0...2000), + in: 0...2000, step: 50, label: {}, minimumValueLabel: { @@ -300,7 +302,7 @@ struct StreamDetailInputScreen: View { Slider( value: $minPlayoutDelay, - in: (0...2000), + in: 0...2000, step: 50, label: {}, minimumValueLabel: { @@ -319,7 +321,7 @@ struct StreamDetailInputScreen: View { Slider( value: $maxPlayoutDelay, - in: (0...2000), + in: 0...2000, step: 50, label: {}, minimumValueLabel: { @@ -347,6 +349,23 @@ struct StreamDetailInputScreen: View { } .pickerStyle(.automatic) } + + HStack { + Text("stream-detail-input.max-bitrate-label", + style: .labelMedium, + font: .custom("AvenirNext-Regular", size: FontSize.body, relativeTo: .body)) + + Picker( + "Maximum Bitrate: \(maxBitrate)", + selection: $maxBitrate + ) { + ForEach(VideoQuality.allCases) { + Text($0.displayText) + .tag($0) + } + } + .pickerStyle(.automatic) + } } .padding() .background(Color(uiColor: themeManager.theme.neutral700)) @@ -404,6 +423,7 @@ struct StreamDetailInputScreen: View { maxPlayoutDelay: nil, disableAudio: disableAudio, primaryVideoQuality: videoQuality, + maxBitrate: maxBitrate, saveLogs: saveLogs )) { let success = viewModel.validateAndSaveStream( @@ -415,6 +435,7 @@ struct StreamDetailInputScreen: View { maxPlayoutDelay: nil, disableAudio: disableAudio, primaryVideoQuality: videoQuality, + maxBitrate: maxBitrate, saveLogs: saveLogs, persistStream: false ) @@ -442,14 +463,14 @@ struct StreamDetailInputScreen: View { } func resetStreamConfigurationState() { - self.useCustomServerURL = false - self.showPlayoutDelay = false - self.disableAudio = false - self.jitterBufferDelayInMs = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) - self.primaryVideoQuality = .auto - self.saveLogs = false - self.minPlayoutDelay = 0 - self.maxPlayoutDelay = 0 + useCustomServerURL = false + showPlayoutDelay = false + disableAudio = false + jitterBufferDelayInMs = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + primaryVideoQuality = .auto + saveLogs = false + minPlayoutDelay = 0 + maxPlayoutDelay = 0 } func syncMaxPlayoutDelay() { @@ -469,6 +490,7 @@ struct StreamDetailInputScreen: View { maxPlayoutDelay = 0 } } + // swiftlint:enable type_body_length extension Font { diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift index 7f5626c9..6afd2725 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift @@ -46,6 +46,7 @@ final class StreamDetailInputViewModel: ObservableObject { maxPlayoutDelay: UInt?, disableAudio: Bool, primaryVideoQuality: VideoQuality, + maxBitrate: UInt, saveLogs: Bool, persistStream: Bool ) -> Bool { @@ -64,6 +65,7 @@ final class StreamDetailInputViewModel: ObservableObject { maxPlayoutDelay: maxPlayoutDelay, disableAudio: disableAudio, primaryVideoQuality: primaryVideoQuality, + maxBitrate: maxBitrate, saveLogs: saveLogs ) ) diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index 435c428b..83c9a10a 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -746,7 +746,10 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = E4A6TDH44W; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -757,6 +760,7 @@ "@executable_path/Frameworks", ); PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From 5f992a7fd32609b32f2cc0cac331931dddecc26d Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Wed, 17 Jul 2024 13:25:21 -0600 Subject: [PATCH 02/19] Pass through max bitrate & delay --- .../RTSCore/Manager/SubscriptionManager.swift | 3 + .../Models/SubscriptionConfiguration.swift | 25 ++--- .../Interactive Viewer/Localizable.strings | 3 +- .../Persistence/StreamDataManager.swift | 12 +-- .../Managers/VideoTracksManager.swift | 3 +- .../RecentStreams/RecentStreamCell.swift | 3 +- .../RecentStreamsViewModel.swift | 22 ++--- .../StreamDetailInputScreen.swift | 94 ++++++++++--------- .../StreamDetailInputViewModel.swift | 14 +-- 9 files changed, 95 insertions(+), 84 deletions(-) diff --git a/LocalPackages/RTSCore/Sources/RTSCore/Manager/SubscriptionManager.swift b/LocalPackages/RTSCore/Sources/RTSCore/Manager/SubscriptionManager.swift index 1d413dea..34b19268 100644 --- a/LocalPackages/RTSCore/Sources/RTSCore/Manager/SubscriptionManager.swift +++ b/LocalPackages/RTSCore/Sources/RTSCore/Manager/SubscriptionManager.swift @@ -80,6 +80,9 @@ public actor SubscriptionManager: ObservableObject { let clientOptions = MCClientOptions() clientOptions.jitterMinimumDelayMs = Int32(configuration.jitterMinimumDelayMs) clientOptions.statsDelayMs = Int32(configuration.statsDelayMs) + if configuration.maxBitrate > 0 { + clientOptions.maximumBitrate = NSNumber(value: configuration.maxBitrate) + } if let rtcEventLogOutputPath = configuration.rtcEventLogPath { clientOptions.rtcEventLogOutputPath = rtcEventLogOutputPath } diff --git a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift index 63e83a4c..5bc307bb 100644 --- a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift +++ b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift @@ -10,6 +10,7 @@ public struct SubscriptionConfiguration { public static let autoReconnect = true public static let jitterMinimumDelayMs: UInt = 20 public static let statsDelayMs: UInt = 1000 + public static let maxBitrate: UInt = 0 public static let disableAudio = false public static let enableStats = true public static let playoutDelay: MCForcePlayoutDelay? = nil @@ -21,27 +22,29 @@ public struct SubscriptionConfiguration { public let autoReconnect: Bool public let jitterMinimumDelayMs: UInt public let statsDelayMs: UInt + public let maxBitrate: UInt public let disableAudio: Bool public let rtcEventLogPath: String? public let sdkLogPath: String? public let enableStats: Bool public let playoutDelay: MCForcePlayoutDelay? - public init( - subscribeAPI: String = Constants.productionSubscribeURL, - autoReconnect: Bool = Constants.autoReconnect, - jitterMinimumDelayMs: UInt = Constants.jitterMinimumDelayMs, - statsDelayMs: UInt = Constants.statsDelayMs, - disableAudio: Bool = Constants.disableAudio, - rtcEventLogPath: String? = nil, - sdkLogPath: String? = nil, - enableStats: Bool = Constants.enableStats, - playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay - ) { + public init(subscribeAPI: String = Constants.productionSubscribeURL, + autoReconnect: Bool = Constants.autoReconnect, + jitterMinimumDelayMs: UInt = Constants.jitterMinimumDelayMs, + statsDelayMs: UInt = Constants.statsDelayMs, + maxBitrate: UInt = Constants.maxBitrate, + disableAudio: Bool = Constants.disableAudio, + rtcEventLogPath: String? = nil, + sdkLogPath: String? = nil, + enableStats: Bool = Constants.enableStats, + playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay) + { self.subscribeAPI = subscribeAPI self.autoReconnect = autoReconnect self.jitterMinimumDelayMs = jitterMinimumDelayMs self.statsDelayMs = statsDelayMs + self.maxBitrate = maxBitrate self.disableAudio = disableAudio self.rtcEventLogPath = rtcEventLogPath self.sdkLogPath = sdkLogPath diff --git a/interactive-player/Interactive Viewer/Localizable.strings b/interactive-player/Interactive Viewer/Localizable.strings index cec9f110..e7e79965 100644 --- a/interactive-player/Interactive Viewer/Localizable.strings +++ b/interactive-player/Interactive Viewer/Localizable.strings @@ -25,7 +25,8 @@ "stream-detail-input.minimum-playout-delay-placeholder-label" = "Minimum Playout Delay"; "stream-detail-input.maximum-playout-delay-placeholder-label" = "Maximum Playout Delay"; "stream-detail-input.primary-video-quality-label" = "Primary video quality:"; -"stream-detail-input.max-bitrate-label" = "Maxium Bitrate:"; +"stream-detail-input.set-max-bitrate-label" = "Set Maximum Bitrate"; +"stream-detail-input.max-bitrate-label" = "Maxium Bitrate (kbps):"; "stream-detail-server-url-label" = "Server URL"; /** App configuration Screen */ diff --git a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift index 9c849488..c50e7d63 100644 --- a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift +++ b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift @@ -17,7 +17,6 @@ protocol StreamDataManagerProtocol: AnyObject { } final class StreamDataManager: NSObject, StreamDataManagerProtocol { - enum StreamDataManagerType { case `default`, testing } @@ -55,10 +54,10 @@ final class StreamDataManager: NSObject, StreamDataManagerProtocol { } self.dateProvider = dateProvider - streamDetailFetchResultsController = NSFetchedResultsController(fetchRequest: Self.recentStreamsFetchRequest, - managedObjectContext: managedObjectContext, - sectionNameKeyPath: nil, - cacheName: nil) + self.streamDetailFetchResultsController = NSFetchedResultsController(fetchRequest: Self.recentStreamsFetchRequest, + managedObjectContext: managedObjectContext, + sectionNameKeyPath: nil, + cacheName: nil) super.init() @@ -146,7 +145,7 @@ final class StreamDataManager: NSObject, StreamDataManagerProtocol { let request: NSFetchRequest = Self.recentStreamsFetchRequest let updatedResults = try coreDataManager.context.fetch(request) if updatedResults.count > Constants.maximumAllowedStreams { - let streamsToDelete = updatedResults[(Constants.maximumAllowedStreams)..) { if let newStreamDetails = controller.fetchedObjects as? [StreamDetailManagedObject] { let streamDetails = newStreamDetails.compactMap { SavedStreamDetail(managedObject: $0) } diff --git a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift index 48c0ef47..8285d41d 100644 --- a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift +++ b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift @@ -141,8 +141,7 @@ final actor VideoTracksManager { Self.logger.debug("♼ Remove view \(view.description) for source \(sourceId)") // Remove view from the list of active views for that source if var activeViews = sourceToActiveViewsMapping[sourceId], - activeViews.contains(where: { $0 == view }) - { + activeViews.contains(where: { $0 == view }) { activeViews.removeAll(where: { $0 == view }) self.sourceToActiveViewsMapping[source.sourceId] = !activeViews.isEmpty ? activeViews : nil } diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift index 18e4b006..82c7bf8c 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift @@ -36,8 +36,7 @@ struct RecentStreamCell: View { private let action: () -> Void init(streamDetail: SavedStreamDetail, - action: @escaping () -> Void) - { + action: @escaping () -> Void) { self.streamDetail = streamDetail self.action = action } diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift index bfd55ef0..5d3185b4 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift @@ -4,12 +4,11 @@ import Combine import Foundation -import RTSCore import MillicastSDK +import RTSCore @MainActor final class RecentStreamsViewModel: ObservableObject { - private let streamDataManager: StreamDataManagerProtocol private let settingsManager: SettingsManager private let dateProvider: DateProvider @@ -21,14 +20,13 @@ final class RecentStreamsViewModel: ObservableObject { topStreamDetails = Array(streamDetails.prefix(3)) } } + @Published private(set) var topStreamDetails: [SavedStreamDetail] = [] @Published private(set) var lastPlayedStream: SavedStreamDetail? - init( - streamDataManager: StreamDataManagerProtocol = StreamDataManager.shared, - settingsManager: SettingsManager = .shared, - dateProvider: DateProvider = DefaultDateProvider() - ) { + init(streamDataManager: StreamDataManagerProtocol = StreamDataManager.shared, + settingsManager: SettingsManager = .shared, + dateProvider: DateProvider = DefaultDateProvider()) { self.streamDataManager = streamDataManager self.settingsManager = settingsManager self.dateProvider = dateProvider @@ -41,7 +39,7 @@ final class RecentStreamsViewModel: ObservableObject { } } } - .store(in: &subscriptions) + .store(in: &subscriptions) } func fetchAllStreams() { @@ -90,16 +88,16 @@ final class RecentStreamsViewModel: ObservableObject { let currentDate = dateProvider.now let rtcLogPath = streamDetail.saveLogs ? URL.rtcLogPath(for: currentDate) : nil let sdkLogPath = streamDetail.saveLogs ? URL.sdkLogPath(for: currentDate) : nil - let playoutDelay: MCForcePlayoutDelay? - if let minPlayoutDelay = streamDetail.minPlayoutDelay, let maxPlayoutDelay = streamDetail.maxPlayoutDelay { + var playoutDelay: MCForcePlayoutDelay? + if let minPlayoutDelay = streamDetail.minPlayoutDelay, + let maxPlayoutDelay = streamDetail.maxPlayoutDelay { playoutDelay = MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) - } else { - playoutDelay = nil } return SubscriptionConfiguration( subscribeAPI: streamDetail.subscribeAPI, jitterMinimumDelayMs: streamDetail.videoJitterMinimumDelayInMs, + maxBitrate: streamDetail.maxBitrate, disableAudio: streamDetail.disableAudio, rtcEventLogPath: rtcLogPath?.path, sdkLogPath: sdkLogPath?.path, diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 244b1c85..1996617e 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -3,6 +3,7 @@ // import DolbyIOUIKit +import MillicastSDK import RTSCore import SwiftUI @@ -19,12 +20,14 @@ struct StreamDetailInputScreen: View { @State private var accountID: String = "" @State private var showAlert = false @State private var useCustomServerURL: Bool = false + @State private var setMaxBitrate: Bool = false @State private var subscribeAPI: String = SubscriptionConfiguration.Constants.developmentSubscribeURL @State private var disableAudio: Bool = false @State private var saveLogs: Bool = false @State private var jitterBufferDelayInMs: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @State private var primaryVideoQuality: VideoQuality = .auto - @State private var maxBitrate: UInt = 0 + @State private var maxBitrateString: String = "0" + @State private var maxBitrate: UInt = SubscriptionConfiguration.Constants.maxBitrate @State private var isShowingSettingsView: Bool = false @State private var showPlayoutDelay: Bool = false @State private var minPlayoutDelay: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @@ -127,9 +130,9 @@ struct StreamDetailInputScreen: View { videoJitterMinimumDelayInMs: videoJitterMinimumDelayInMs, minPlayoutDelay: minPlayoutDelay, maxPlayoutDelay: maxPlayoutDelay, + maxBitrate: maxBitrate, disableAudio: disableAudio, primaryVideoQuality: primaryVideoQuality, - maxBitrate: maxBitrate, saveLogs: saveLogs, persistStream: true ) @@ -143,6 +146,7 @@ struct StreamDetailInputScreen: View { videoJitterMinimumDelayInMs: videoJitterMinimumDelayInMs, minPlayoutDelay: minPlayoutDelay, maxPlayoutDelay: maxPlayoutDelay, + maxBitrate: maxBitrate, disableAudio: disableAudio, primaryVideoQuality: primaryVideoQuality, saveLogs: saveLogs @@ -235,6 +239,10 @@ struct StreamDetailInputScreen: View { .onChange(of: maxPlayoutDelay) { _ in syncMinPlayoutDelay() } + .onChange(of: maxBitrateString) { bitrateString in + guard let bitrate = Int(bitrateString) else { return } + maxBitrate = UInt(bitrate) + } } var additionalConfigurationView: some View { @@ -350,21 +358,19 @@ struct StreamDetailInputScreen: View { .pickerStyle(.automatic) } - HStack { - Text("stream-detail-input.max-bitrate-label", - style: .labelMedium, - font: .custom("AvenirNext-Regular", size: FontSize.body, relativeTo: .body)) + Toggle(isOn: $setMaxBitrate) { + Text( + "stream-detail-input.set-max-bitrate-label", + font: .streamConfigurationItemsFont + ) + } - Picker( - "Maximum Bitrate: \(maxBitrate)", - selection: $maxBitrate - ) { - ForEach(VideoQuality.allCases) { - Text($0.displayText) - .tag($0) - } - } - .pickerStyle(.automatic) + if setMaxBitrate { + DolbyIOUIKit.TextField(text: $maxBitrateString, placeholderText: "stream-detail-input.max-bitrate-label") + .keyboardType(.numberPad) + .accessibilityIdentifier("InputScreen.MaximumBitrate") + .font(.avenirNextRegular(withStyle: .caption, size: FontSize.caption1)) + .submitLabel(.next) } } .padding() @@ -413,45 +419,46 @@ struct StreamDetailInputScreen: View { let disableAudio = false let videoQuality = VideoQuality.auto let saveLogs = false - - RecentStreamCell(streamDetail: SavedStreamDetail( - accountID: accountID, - streamName: streamName, - subscribeAPI: productionSubscribeURL, - videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: nil, - maxPlayoutDelay: nil, - disableAudio: disableAudio, - primaryVideoQuality: videoQuality, - maxBitrate: maxBitrate, - saveLogs: saveLogs - )) { - let success = viewModel.validateAndSaveStream( - streamName: streamName, - accountID: accountID, - subscribeAPI: productionSubscribeURL, - videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: nil, - maxPlayoutDelay: nil, - disableAudio: disableAudio, - primaryVideoQuality: videoQuality, - maxBitrate: maxBitrate, - saveLogs: saveLogs, - persistStream: false - ) + let playoutDelayMin = showPlayoutDelay ? UInt(minPlayoutDelay) : nil + let playoutDelayMax = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil + + RecentStreamCell(streamDetail: SavedStreamDetail(accountID: accountID, + streamName: streamName, + subscribeAPI: productionSubscribeURL, + videoJitterMinimumDelayInMs: jitterMinimumDelayMs, + minPlayoutDelay: playoutDelayMin, + maxPlayoutDelay: playoutDelayMax, + disableAudio: disableAudio, + primaryVideoQuality: videoQuality, + maxBitrate: maxBitrate, + saveLogs: saveLogs)) { + let success = viewModel.validateAndSaveStream(streamName: streamName, + accountID: accountID, + subscribeAPI: productionSubscribeURL, + videoJitterMinimumDelayInMs: jitterMinimumDelayMs, + minPlayoutDelay: playoutDelayMin, + maxPlayoutDelay: playoutDelayMax, + maxBitrate: maxBitrate, + disableAudio: disableAudio, + primaryVideoQuality: videoQuality, + saveLogs: saveLogs, + persistStream: false) guard success else { showAlert = true return } + let playoutDelay: MCForcePlayoutDelay? = showPlayoutDelay ? MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) : nil + let configuration = SubscriptionConfiguration( subscribeAPI: productionSubscribeURL, jitterMinimumDelayMs: jitterMinimumDelayMs, + maxBitrate: maxBitrate, disableAudio: disableAudio, rtcEventLogPath: nil, sdkLogPath: nil, - playoutDelay: nil + playoutDelay: playoutDelay ) streamingScreenContext = StreamingView.Context( streamName: streamName, @@ -471,6 +478,7 @@ struct StreamDetailInputScreen: View { saveLogs = false minPlayoutDelay = 0 maxPlayoutDelay = 0 + maxBitrate = SubscriptionConfiguration.Constants.maxBitrate } func syncMaxPlayoutDelay() { diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift index 6afd2725..b0dc632e 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift @@ -3,9 +3,9 @@ // import Combine -import RTSCore import Foundation import MillicastSDK +import RTSCore @MainActor final class StreamDetailInputViewModel: ObservableObject { @@ -44,9 +44,9 @@ final class StreamDetailInputViewModel: ObservableObject { videoJitterMinimumDelayInMs: UInt, minPlayoutDelay: UInt?, maxPlayoutDelay: UInt?, + maxBitrate: UInt, disableAudio: Bool, primaryVideoQuality: VideoQuality, - maxBitrate: UInt, saveLogs: Bool, persistStream: Bool ) -> Bool { @@ -78,15 +78,15 @@ final class StreamDetailInputViewModel: ObservableObject { videoJitterMinimumDelayInMs: UInt, minPlayoutDelay: UInt?, maxPlayoutDelay: UInt?, + maxBitrate: UInt, disableAudio: Bool, primaryVideoQuality: VideoQuality, saveLogs: Bool ) -> SubscriptionConfiguration { - let playoutDelay: MCForcePlayoutDelay? - if let minPlayoutDelay, let maxPlayoutDelay { + var playoutDelay: MCForcePlayoutDelay? + if let minPlayoutDelay, + let maxPlayoutDelay { playoutDelay = MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) - } else { - playoutDelay = nil } let currentDate = dateProvider.now let rtcLogPath = saveLogs ? URL.rtcLogPath(for: currentDate) : nil @@ -95,12 +95,14 @@ final class StreamDetailInputViewModel: ObservableObject { return SubscriptionConfiguration( subscribeAPI: subscribeAPI, jitterMinimumDelayMs: videoJitterMinimumDelayInMs, + maxBitrate: maxBitrate, disableAudio: disableAudio, rtcEventLogPath: rtcLogPath?.path, sdkLogPath: sdkLogPath?.path, playoutDelay: playoutDelay ) } + // swiftlint:enable function_parameter_count func clearAllStreams() { From 36d22dce4eb4feb47b0deabb6b04f4d8167e50b7 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Wed, 17 Jul 2024 13:40:50 -0600 Subject: [PATCH 03/19] add .DS_Store to gitignore --- .DS_Store | Bin 6148 -> 6148 bytes interactive-player/.gitignore | 1 + rts-viewer-ios/.DS_Store | Bin 0 -> 6148 bytes rts-viewer-ios/.gitignore | 1 + .../RTSViewer.xcodeproj/project.pbxproj | 4 ++++ rts-viewer-tvos/.gitignore | 1 + 6 files changed, 7 insertions(+) create mode 100644 rts-viewer-ios/.DS_Store diff --git a/.DS_Store b/.DS_Store index 0f0324f93566a001a32cb4108e7c9fe65f84adeb..6ad7bbaec594b1cb695062716d735a1e7f152fda 100644 GIT binary patch delta 477 zcmZoMXfc=|#>B!ku~2NHo}wrl0|Nsi1A_nqLlHwFLo!1NLncGnW<%!X%%&hIAs{yo zD42>Y4dm%E6foofHB^FRlFEwl-Q7|*KsMS%Zwlp%( zQ7|z!tF7ha5LMQ<4vNpt$<52}25JBTMxgy*zzd~eR1c6rlmp6wi}G^v^U{IpL1r=X zO%`Ah7tCbHXDCK@n-3M;hGY%03nue0=`w-?!h>3t0gY6eEXI6bGdl-A2Qc!05%isT UGQWr;2hhJvpzzupA+m-U0E_urb^rhX delta 150 zcmZoMXfc=|#>B)qu~2NHo}wrd0|Nsi1A_nqLm5LRLn=c#5EpGMT+TSzK!jzoBbyfE zuE`E8g_=^;)rJ-(7CH(hhGw-o3e|>2riMBSW|n5PwVWKH%KFwp@!2`KdHFq)Z?ecU y_D;5AQ=06~eqgf#2RqBe2CdEP9Q+(WTQ>`Gd}p4_FXG4nv=?MC%jO7?HOv5nha=7a diff --git a/interactive-player/.gitignore b/interactive-player/.gitignore index b8fcc057..8e36cb12 100644 --- a/interactive-player/.gitignore +++ b/interactive-player/.gitignore @@ -1,6 +1,7 @@ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore +.DS_Store ## User settings xcuserdata/ diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..47fd0472e44c3093ed8649f13e2c6f24436d665a GIT binary patch literal 6148 zcmeHKJxc>Y5Pcg{L~H^gSYBacBjz8R;j9BDSXgO3@GIeth{p0PEo}W8fi!WFuMC7IObDU#< z34M&6h0zxHiwelzt>XY4v~h^}{kugEDf-;ajQg45VtCqU_tH_OG+yt%pE*jCq&rNz z%&29pr_T1)bgxWyf0q6Ja&!N@S|&R?oBP#H{^!_O%6CGZV_f2joKMJqP~`kLEtfsd z`(gK}6?m6ef0TZ5|Je)${&*AqW~P8CU<$Bii`CW?tu+Nq0aIY6fP5b!x?mhISM;9_ zHvS4gtTOD4eSgLxMiL;#0dqx0Xiig!n(D$8!)ZF>Q5Y8o%oQ~q&acdQ!pbh(Q1pby zc;tt}#fsLN0;WJwffaqMOa0$pe*Z5f*^? Date: Wed, 17 Jul 2024 13:42:54 -0600 Subject: [PATCH 04/19] .DS_Store removal --- rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ .../xcshareddata/swiftpm/Package.resolved | 16 +++++++++++++--- rts-viewer-tvos/.DS_Store | Bin 6148 -> 6148 bytes 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 47fd0472e44c3093ed8649f13e2c6f24436d665a..c43ed32394c8953c0fe909a170c5fd37ff99ca09 100644 GIT binary patch delta 73 zcmZoMXffE}!o>8ecrp)@*5m>vugNt`Mw8z#@i93tOy0+=H(86Bk7>FKn3=)M!&;lj Uz^XlYH?!g7RWRP>-OQdM07NkrcK`qY delta 76 zcmZoMXffE}!o>8XaWW5+*5m>vFScKI%Q`o4PCme7#U#Kmc^|XhWG!YsrfJ8(%nW88 Y)?R}sf6d9enGGkeg7G%*X7&^T0PIm2761SM diff --git a/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3fe42100..e04b0f64 100644 --- a/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,14 +1,24 @@ { + "originHash" : "350f3a4231a91e6b70c75f0177fae04514d6b537cbf4d413120edf515f4c9355", "pins" : [ { "identity" : "millicast-sdk-swift-package", "kind" : "remoteSourceControl", "location" : "https://github.com/millicast/millicast-sdk-swift-package", "state" : { - "revision" : "83c18963b46cbabcb37bde44198acc8218d48d3a", - "version" : "1.4.2" + "revision" : "8e8feebf07a75e60f4016498de00f60c586fa247", + "version" : "1.8.5" + } + }, + { + "identity" : "rts-uikit-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/DolbyIO/rts-uikit-ios", + "state" : { + "revision" : "e5a2a8150dcc79a14d13a8df2ecdf15c7a6e5794", + "version" : "0.0.4" } } ], - "version" : 2 + "version" : 3 } diff --git a/rts-viewer-tvos/.DS_Store b/rts-viewer-tvos/.DS_Store index f430b74f64b8b47e11b1e0b7d92e3b0f26c228ea..bba5921baa5c2595f9f7322b8ae9bfd56e24bc04 100644 GIT binary patch delta 56 zcmZoMXffDe!OXO9!{mL;dXqDldDt#qWtsX!Y4RRsgUPFqxwec14(8f2F+SKV$ic(1 JnVsV=KL8yW5t0A^ From 0f735323c3f5714be742438cbb8512741df10b6b Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Wed, 17 Jul 2024 14:00:49 -0600 Subject: [PATCH 05/19] revert profile --- .../RTSViewer.xcodeproj/project.pbxproj | 7 +++++-- rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes .../RTSViewer.xcodeproj/project.pbxproj | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index 83c9a10a..f65b9fb2 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -747,9 +747,11 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = E4A6TDH44W; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = E4A6TDH44W; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -761,6 +763,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Dolby IO iOS Interactive Player Development"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index c43ed32394c8953c0fe909a170c5fd37ff99ca09..5dd60a94b7caca422a8ba1000008d8c0a4702ec2 100644 GIT binary patch delta 89 zcmZoMXffE}!o>7VY%&j%*5m>v9@fY?L0Y<#8<{LweLv=^Y6Drilix7$F&QvS-p8yr jS&NyEX~_*RGlQ9jH6zAGL3i?QX2Z#=V7$${nLR}S#eN#u delta 89 zcmZoMXffE}!o>8ecrp)@*5m>v9@YZ?^&y&*8<{NOEZxa(nE03+7$)yy)|;%w%*Qm{ d1 Date: Wed, 17 Jul 2024 14:12:40 -0600 Subject: [PATCH 06/19] cleanup --- .../StreamDetailInputScreen.swift | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 1996617e..dc5540eb 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -24,14 +24,14 @@ struct StreamDetailInputScreen: View { @State private var subscribeAPI: String = SubscriptionConfiguration.Constants.developmentSubscribeURL @State private var disableAudio: Bool = false @State private var saveLogs: Bool = false - @State private var jitterBufferDelayInMs: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var jitterBufferDelayInMs = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @State private var primaryVideoQuality: VideoQuality = .auto @State private var maxBitrateString: String = "0" @State private var maxBitrate: UInt = SubscriptionConfiguration.Constants.maxBitrate @State private var isShowingSettingsView: Bool = false @State private var showPlayoutDelay: Bool = false - @State private var minPlayoutDelay: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) - @State private var maxPlayoutDelay: Float = .init(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var minPlayoutDelay = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var maxPlayoutDelay: = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @FocusState private var inputFocus: InputFocusable? @@ -422,27 +422,31 @@ struct StreamDetailInputScreen: View { let playoutDelayMin = showPlayoutDelay ? UInt(minPlayoutDelay) : nil let playoutDelayMax = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil - RecentStreamCell(streamDetail: SavedStreamDetail(accountID: accountID, - streamName: streamName, - subscribeAPI: productionSubscribeURL, - videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: playoutDelayMin, - maxPlayoutDelay: playoutDelayMax, - disableAudio: disableAudio, - primaryVideoQuality: videoQuality, - maxBitrate: maxBitrate, - saveLogs: saveLogs)) { - let success = viewModel.validateAndSaveStream(streamName: streamName, - accountID: accountID, - subscribeAPI: productionSubscribeURL, - videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: playoutDelayMin, - maxPlayoutDelay: playoutDelayMax, - maxBitrate: maxBitrate, - disableAudio: disableAudio, - primaryVideoQuality: videoQuality, - saveLogs: saveLogs, - persistStream: false) + RecentStreamCell(streamDetail: SavedStreamDetail( + accountID: accountID, + streamName: streamName, + subscribeAPI: productionSubscribeURL, + videoJitterMinimumDelayInMs: jitterMinimumDelayMs, + minPlayoutDelay: playoutDelayMin, + maxPlayoutDelay: playoutDelayMax, + disableAudio: disableAudio, + primaryVideoQuality: videoQuality, + maxBitrate: maxBitrate, + saveLogs: saveLogs + )) { + let success = viewModel.validateAndSaveStream( + streamName: streamName, + accountID: accountID, + subscribeAPI: productionSubscribeURL, + videoJitterMinimumDelayInMs: jitterMinimumDelayMs, + minPlayoutDelay: playoutDelayMin, + maxPlayoutDelay: playoutDelayMax, + maxBitrate: maxBitrate, + disableAudio: disableAudio, + primaryVideoQuality: videoQuality, + saveLogs: saveLogs, + persistStream: false + ) guard success else { showAlert = true From 9c5b28cb4d8eab733a8d435af8456984eaf0a479 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Thu, 18 Jul 2024 11:38:37 -0600 Subject: [PATCH 07/19] PR fixes --- .DS_Store | Bin 6148 -> 10244 bytes .../RTSViewer.xcdatamodel/contents | 2 +- .../Persistence/StreamDataManager.swift | 2 +- .../Models/SavedStreamDetail.swift | 6 +++--- .../RecentStreams/RecentStreamCell.swift | 8 ++++++-- .../RecentStreamsViewModel.swift | 2 +- .../StreamDetailInputScreen.swift | 18 ++++++++---------- .../RTSViewer.xcodeproj/project.pbxproj | 3 +++ rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes 9 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.DS_Store b/.DS_Store index 6ad7bbaec594b1cb695062716d735a1e7f152fda..67cdcba5ae189ef5ace8adb2ab275bdfd0403b5a 100644 GIT binary patch literal 10244 zcmeHMTWl0n7(V~B&@;oOr%*0~T^%S?pe-!7LIq{JrQESd@8z=2?hJIobY|I^-31$B z6*L-&QD4*;i2ACiJV+uB#5eK92h$kQhz};d=z}Icp^5%y&MesFV&g5<%p`OEIsf_p zGv|Eg`{(R`mJkBTys?T9jSxcd=8{dv;x-p==l3m_6l!FYfc}K!h)y!ZCl)E(VjU?W z1R?|?1R?|?1R?|`0|K~b^NUUsGAbhkA_O7?ZXm$F4;tQFCPFzXWbo<0B1i#HR-!t% z@EP-fz()&ZB9x;-2Cmqr$Q~ecg)qec;g0PZH%FNW<*1Otoq%vB5WX403qn{R- z6G#Xdl@S6F0=FW-bN5-8uREBvo8$6#lQfeou`#1R!CTTrFPpaAG}>QGXKl;#vml`U3%8jy_Q)_vD$Xm@pa2FbAf|pm}qjK z&vFd6m~``w;RU{3qJhd(PO-ToBk?9}ZR`4zP1;E7YW}>Y<>X13&Z%FyabN1FJ?yy8 zh{-|tfgm%=Cj0C}XKHvEJB=gf9Zd)t+bgsSXew$-(0pEkMpmY&J-teQip?tE{5!M0 zSp{mGQhecP_iu^BU~nWD_D z?JHQGmA1_R-OHGcVL3;7ik9II4dNm=zIE6#bFy3mP2G+@Y>v&HH=n5s)Vh+Fu)XO^ zSv~E~r>$do%ReFBp=t|d%HBU<-~jiy2Jg`bT2xhALg{|4Z_k-n3bmpxTaFHTw}bXmYVfk)OprNS`Q#!yqYcmY& zhs~Qmckd&ripyg;1q-7BS6c)J`;<~zpI9yyCqPuLPsGEuqC_PtaL?OL4q((}$tiM% zoF(VTd2*3lCRfNObzerQ1YH5+QSXv@ANcT%|X`R#}#x0eiZeTp;q|I2LXb^?p7sd-Li4>4RQ%Cmgx6;S_e!X$NzxI!TOi85CcqIpwz{B zkm}~~n}H<)trT{wiiafS1XS-+7RJ~V3fR15@BT^4JMF$v>OR9D2}3ah~A zk^|p0u||af6_xc$tHLTV&O~*SQl~H?i%KGvj4>WHe}cak$R+Y2@^=jR`z`s7{6Kz1 z&d!AS$kzr~3d^7gnvt^`pcOX4Cg^}|upM?HgS(*@`d|PCAp<7zcL*%Vfe(2oz~k@) zviLMSg&ckXUWAw6H8>0B;B|N-VDo!$p^Tq9#`6;{O_ZO7uAF1L&JncdT%U=1rB6KE zBMZ2Dnxq))p3V5|67PSpSKf7oWoT620SzC~8IQ*YrP%)0emkfmBdgc9v^Hrit2W%_ z=fiV?7(VJOBSb$wnOwCqf`X z;O|5LDz|iPNn($sB)+_Je%7wx{V3l2;&!7#1}<1|6Rr=A{R2kt1vY5qU{XTV=NM2qtODF6TO=l{O}SR*4c delta 120 zcmZn(XfcprU|?W$DortDU=RQ@Ie-{Mvv5r;6q~50$jG)aU^g=(+hiUAPaoNpI5?2`)vvY6=G6Qu0fdDs7&gcA%wYxq Db6XYZ diff --git a/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents b/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents index fad55309..577c5ddb 100644 --- a/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents +++ b/interactive-player/Interactive Viewer/Managers/Persistence/RTSViewer.xcdatamodeld/RTSViewer.xcdatamodel/contents @@ -4,7 +4,7 @@ - + diff --git a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift index c50e7d63..8d463d55 100644 --- a/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift +++ b/interactive-player/Interactive Viewer/Managers/Persistence/StreamDataManager.swift @@ -138,7 +138,7 @@ final class StreamDataManager: NSObject, StreamDataManagerProtocol { streamDetailToSave.maxPlayoutDelay = streamDetail.maxPlayoutDelay.map { NSNumber(value: $0) } streamDetailToSave.disableAudio = streamDetail.disableAudio streamDetailToSave.primaryVideoQuality = streamDetail.primaryVideoQuality.rawValue - streamDetailToSave.maxBitrate = Int32(streamDetail.maxBitrate) + streamDetailToSave.maxBitrate = streamDetail.maxBitrate.map { NSNumber(value: $0) } streamDetailToSave.saveLogs = streamDetail.saveLogs // Delete streams that are older and exceeding the maximum allowed count diff --git a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift index ed673e0b..c8522815 100644 --- a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift +++ b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift @@ -15,7 +15,7 @@ struct SavedStreamDetail: Identifiable, Equatable { let maxPlayoutDelay: UInt? let disableAudio: Bool let primaryVideoQuality: VideoQuality - let maxBitrate: UInt + let maxBitrate: UInt? let saveLogs: Bool init(accountID: String, @@ -59,7 +59,7 @@ extension SavedStreamDetail { let maxPlayoutDelay = managedObject.maxPlayoutDelay let disableAudio = managedObject.disableAudio let saveLogs = managedObject.saveLogs - let maxBitrate = managedObject.maxBitrate + let maxBitrate = managedObject.maxBitrate ?? 0 self.id = UUID() self.accountID = accountID @@ -71,7 +71,7 @@ extension SavedStreamDetail { self.maxPlayoutDelay = maxPlayoutDelay.map { UInt(truncating: $0) } self.disableAudio = disableAudio self.primaryVideoQuality = primaryVideoQuality - self.maxBitrate = UInt(maxBitrate) + self.maxBitrate = UInt(truncating: maxBitrate) self.saveLogs = saveLogs } } diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift index 82c7bf8c..96252baf 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift @@ -22,10 +22,14 @@ struct RecentStreamCell: View { (String(localized: "recent-streams.server-url.label"), String(streamDetail.subscribeAPI)), (String(localized: "recent-streams.video-jitter-buffer.label"), String(streamDetail.videoJitterMinimumDelayInMs)), (String(localized: "recent-streams.min-playout-delay.label"), streamDetail.minPlayoutDelay.map { String($0) } ?? "N/A"), - (String(localized: "recent-streams.max-playout-delay.label"), streamDetail.maxPlayoutDelay.map { String($0) } ?? "N/A"), + (String(localized: "recent-streams.max-playout-delay.label"), streamDetail.maxPlayoutDelay.map { String($0) } ?? "N/A") + ] + ) + fields.append( + contentsOf: [ (String(localized: "recent-streams.disable-audio.label"), String(streamDetail.disableAudio)), (String(localized: "recent-streams.primary-video-quality.label"), streamDetail.primaryVideoQuality.displayText), - (String(localized: "recent-streams.max-bitrate.label"), String(streamDetail.maxBitrate)), + (String(localized: "recent-streams.max-bitrate.label"), String(streamDetail.maxBitrate ?? 0)), (String(localized: "recent-streams.save-logs.label"), String(streamDetail.saveLogs)) ] ) diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift index 5d3185b4..ea02dcef 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift @@ -97,7 +97,7 @@ final class RecentStreamsViewModel: ObservableObject { return SubscriptionConfiguration( subscribeAPI: streamDetail.subscribeAPI, jitterMinimumDelayMs: streamDetail.videoJitterMinimumDelayInMs, - maxBitrate: streamDetail.maxBitrate, + maxBitrate: streamDetail.maxBitrate ?? 0, disableAudio: streamDetail.disableAudio, rtcEventLogPath: rtcLogPath?.path, sdkLogPath: sdkLogPath?.path, diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index dc5540eb..65aee17e 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -20,18 +20,17 @@ struct StreamDetailInputScreen: View { @State private var accountID: String = "" @State private var showAlert = false @State private var useCustomServerURL: Bool = false - @State private var setMaxBitrate: Bool = false + @State private var isShowingMaxBitrate: Bool = false @State private var subscribeAPI: String = SubscriptionConfiguration.Constants.developmentSubscribeURL @State private var disableAudio: Bool = false @State private var saveLogs: Bool = false @State private var jitterBufferDelayInMs = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @State private var primaryVideoQuality: VideoQuality = .auto @State private var maxBitrateString: String = "0" - @State private var maxBitrate: UInt = SubscriptionConfiguration.Constants.maxBitrate @State private var isShowingSettingsView: Bool = false @State private var showPlayoutDelay: Bool = false @State private var minPlayoutDelay = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) - @State private var maxPlayoutDelay: = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) + @State private var maxPlayoutDelay = Float(SubscriptionConfiguration.Constants.jitterMinimumDelayMs) @FocusState private var inputFocus: InputFocusable? @@ -122,6 +121,7 @@ struct StreamDetailInputScreen: View { let videoJitterMinimumDelayInMs = UInt(jitterBufferDelayInMs) let minPlayoutDelay = showPlayoutDelay ? UInt(minPlayoutDelay) : nil let maxPlayoutDelay = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil + let maxBitrate: UInt = UInt(maxBitrateString) ?? 0 let success = viewModel.validateAndSaveStream( streamName: streamName, @@ -239,10 +239,6 @@ struct StreamDetailInputScreen: View { .onChange(of: maxPlayoutDelay) { _ in syncMinPlayoutDelay() } - .onChange(of: maxBitrateString) { bitrateString in - guard let bitrate = Int(bitrateString) else { return } - maxBitrate = UInt(bitrate) - } } var additionalConfigurationView: some View { @@ -358,14 +354,14 @@ struct StreamDetailInputScreen: View { .pickerStyle(.automatic) } - Toggle(isOn: $setMaxBitrate) { + Toggle(isOn: $isShowingMaxBitrate) { Text( "stream-detail-input.set-max-bitrate-label", font: .streamConfigurationItemsFont ) } - if setMaxBitrate { + if isShowingMaxBitrate { DolbyIOUIKit.TextField(text: $maxBitrateString, placeholderText: "stream-detail-input.max-bitrate-label") .keyboardType(.numberPad) .accessibilityIdentifier("InputScreen.MaximumBitrate") @@ -421,6 +417,7 @@ struct StreamDetailInputScreen: View { let saveLogs = false let playoutDelayMin = showPlayoutDelay ? UInt(minPlayoutDelay) : nil let playoutDelayMax = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil + let maxBitrate: UInt = UInt(maxBitrateString) ?? 0 RecentStreamCell(streamDetail: SavedStreamDetail( accountID: accountID, @@ -434,6 +431,7 @@ struct StreamDetailInputScreen: View { maxBitrate: maxBitrate, saveLogs: saveLogs )) { + let success = viewModel.validateAndSaveStream( streamName: streamName, accountID: accountID, @@ -482,7 +480,7 @@ struct StreamDetailInputScreen: View { saveLogs = false minPlayoutDelay = 0 maxPlayoutDelay = 0 - maxBitrate = SubscriptionConfiguration.Constants.maxBitrate + maxBitrateString = "0" } func syncMaxPlayoutDelay() { diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index f65b9fb2..88e94356 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -647,6 +647,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_BITCODE = NO; @@ -712,6 +713,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_BITCODE = NO; @@ -839,6 +841,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_BITCODE = NO; diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 5dd60a94b7caca422a8ba1000008d8c0a4702ec2..9a6ec32afab16733d1d4a2151f899ce184a07526 100644 GIT binary patch delta 55 zcmZoMXffDe!OV1R|KxqldXqDlc~~DRUNAZ|c{j7+dXqDlc~~=IY!q}S?`Affyb6hD$Heeqvmgf#%Vu_tzx)99 C9uWHg From db7e34596abd70807533b24c60a5583bf6268a88 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Thu, 18 Jul 2024 11:48:02 -0600 Subject: [PATCH 08/19] format change --- .../Models/SubscriptionConfiguration.swift | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift index 5bc307bb..21eb50e3 100644 --- a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift +++ b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift @@ -29,16 +29,17 @@ public struct SubscriptionConfiguration { public let enableStats: Bool public let playoutDelay: MCForcePlayoutDelay? - public init(subscribeAPI: String = Constants.productionSubscribeURL, - autoReconnect: Bool = Constants.autoReconnect, - jitterMinimumDelayMs: UInt = Constants.jitterMinimumDelayMs, - statsDelayMs: UInt = Constants.statsDelayMs, - maxBitrate: UInt = Constants.maxBitrate, - disableAudio: Bool = Constants.disableAudio, - rtcEventLogPath: String? = nil, - sdkLogPath: String? = nil, - enableStats: Bool = Constants.enableStats, - playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay) + public init( + subscribeAPI: String = Constants.productionSubscribeURL, + autoReconnect: Bool = Constants.autoReconnect, + jitterMinimumDelayMs: UInt = Constants.jitterMinimumDelayMs, + statsDelayMs: UInt = Constants.statsDelayMs, + maxBitrate: UInt = Constants.maxBitrate, + disableAudio: Bool = Constants.disableAudio, + rtcEventLogPath: String? = nil, + sdkLogPath: String? = nil, + enableStats: Bool = Constants.enableStats, + playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay) { self.subscribeAPI = subscribeAPI self.autoReconnect = autoReconnect From bcec92a0ca9eaeaa88a10856a67f8a73c78bf1e1 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Thu, 18 Jul 2024 12:07:28 -0600 Subject: [PATCH 09/19] toggle fix --- .../Views/StreamDetailInputScreen/StreamDetailInputScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 65aee17e..1276b9a7 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -417,7 +417,7 @@ struct StreamDetailInputScreen: View { let saveLogs = false let playoutDelayMin = showPlayoutDelay ? UInt(minPlayoutDelay) : nil let playoutDelayMax = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil - let maxBitrate: UInt = UInt(maxBitrateString) ?? 0 + let maxBitrate: UInt = isShowingMaxBitrate ? UInt(maxBitrateString) ?? 0 : 0 RecentStreamCell(streamDetail: SavedStreamDetail( accountID: accountID, From 6e5194e73c61563d6efb8259d25b3cfc0a97e472 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Thu, 18 Jul 2024 17:35:55 -0600 Subject: [PATCH 10/19] xcodeproj fixes --- .../RTSViewer.xcodeproj/project.pbxproj | 7 ------- .../xcshareddata/swiftpm/Package.resolved | 14 -------------- 2 files changed, 21 deletions(-) delete mode 100644 interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index 88e94356..cf0d7350 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -748,12 +748,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = E4A6TDH44W; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -764,8 +759,6 @@ "@executable_path/Frameworks", ); PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Dolby IO iOS Interactive Player Development"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index c9be3839..00000000 --- a/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "millicast-sdk-swift-package", - "kind" : "remoteSourceControl", - "location" : "https://github.com/millicast/millicast-sdk-swift-package", - "state" : { - "revision" : "320331b070210fd41818e2e9459c2631991b6c16", - "version" : "2.0.0-beta.5" - } - } - ], - "version" : 2 -} From bf840ec7471600fce79c845214280efd18f44041 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Thu, 18 Jul 2024 17:39:02 -0600 Subject: [PATCH 11/19] xcodeproj fix --- .DS_Store | Bin 10244 -> 12292 bytes rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes .../RTSViewer.xcodeproj/project.pbxproj | 6 ------ 3 files changed, 6 deletions(-) diff --git a/.DS_Store b/.DS_Store index 67cdcba5ae189ef5ace8adb2ab275bdfd0403b5a..4874931db77dd7675928c95ba1c15d1d013758ac 100644 GIT binary patch delta 1533 zcmbW1OKclO7{}+ocIY?8KW`wxc#}984a~qp9j94K!_DtrOye(8SIzWQ$|R zUgt#$u2A97Qc$!9-Uv=SDk_CgXnUa&6aq&YkctozMIauBR)Pzq2WGtqLIMsj((KIt z`)0m*e7~KO11BfEgb;MhN_P?BVqKn9y`_ch^8_JYVcEM)G%*2Ix3bFmln|8!$sAFL zOy-xi5E2F-Qj(I5s>a*~p-CJ{#Nu+oMkQmR7NJcXo|7{w zJGqS>GK_nKQZYK2nx19{Qf-E9pV%oL)z|HbsoDM+IjQey&G6mLe52}AEF<^NC*@3j zOF2jJL1xzPP%JjXZ$#&N^(xg#`J_l zSTn2geuhWB_`^FkI(u pkF~;gBI2nIg&6ll`QJOppv&B4^06mBdr*Q^P;TgPy zm+=Z-#hZ8=t2mE$@jlk@2|mTudvP7#;yZkg8@PqvahogP$~YJ2=6qZ&7v$==X0C;6 z<92g>Tt64(V%#w<#myKe;Pf9vFZUbG=9gRCBz(pooICf=gnRBe(0x$o`Co*$mRu{! zL+vcRqs&{qLBLyRnWf!7JA)^)?>5AcjETCgFv*D!gA zS`6t4*6|Eu%H^ftsc*0`SgC142Ai3LA%xxSHbyNqZ^+6plO!)Zp8Z`c%vz4}{}qXU zBjOslPJSRa$!$hN5lY~~HiQ@l9XNo#JP8pD;V8y2fjA^gBZ<@$(hQ2@cm$8)1Ri5Z zJjtMV8q0VdFW^N6#%p*TZ{RH6VpzO~3%G<2@FA|?V*?O;#pw76KO4FIGvmFRxi(t- zs%KmqcJFl;7#5fQ+;;u-*{Du27?PiB@P%cR;h#YOT`2gJ=oo&ebE1tCp3lW?eqf{RbyVWF!Cp delta 216 zcmZokXbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~3gIoUvlMH0wo-~?hupg03V5kn$F zGD8VNCPUd~Lyg7ko7p*7I2bu6^UG`SB$XEzB<18M0TpkX?4VFM`GLB}WFdJ~&XnTh zoTU8x9LCA{D%zVv6)&?*oGd$8N5FEjy+SFck-3?rj)Iw?#pJyT_LCI_$R6WK708g9D>SP`!t;q#UJgoMn>x>Pjj0Z5}9;Q#;t delta 76 zcmZoMXffE}!o>7VY%&j%*5m>v9@fY?L0Y<#8=1B-GhEw0c^|Xh Date: Thu, 18 Jul 2024 17:43:59 -0600 Subject: [PATCH 12/19] project fixes --- rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes .../RTSViewer.xcodeproj/project.pbxproj | 1 - 2 files changed, 1 deletion(-) diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 34a456dccf1937eefd2ba8d031df1bb06693b465..013389cea0d2946f5e0e23fda916bf25b1f8383e 100644 GIT binary patch delta 53 zcmZoMXffE}%*4#d*fyDmNo#Te6A#OdZPxE5H!xYj7 Date: Fri, 19 Jul 2024 09:59:35 -0600 Subject: [PATCH 13/19] change guard statement --- .DS_Store | Bin 12292 -> 10244 bytes .../Managers/VideoTracksManager.swift | 8 ++++++-- .../xcshareddata/swiftpm/Package.resolved | 15 +++++++++++++++ rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.DS_Store b/.DS_Store index 4874931db77dd7675928c95ba1c15d1d013758ac..54907672e704e63d9201d7ad3b43413c6a5276dd 100644 GIT binary patch delta 1393 zcmd^9U2GIp6h7Z>vG=Ck?v3C0jjgZQFPK8PeHNMlR{^-01L2@o{|(7QVe4<>|!S0=gl zoAcc{_ni68xp#iw{2P@-ge_UEktiTvnH+f(<+*7k_lnPs`b&J$CXXDDJW+_ zG_%+k{-kuVl$9`eY;SegZVnt}O>MDApmlwd{Y@YV*y-B(md->nogi}ENlYaT(~3ra zfy-U8?1fUsd6}|faA%s;W8|Zde!F*wNLuZOK#N6`b44Yoc==Z-r1+D?TQIY&4rNrn83i zw)26auHj5v*rUlf+tZq{-_W(filV4BjP1_zof%!u4$YNNopgu~nKF}NjQR-Y!W??b zNEKaqw2^V44JYFXJ-OtWlbI;IrB!nMJ7=7Hofv$@2F6Dx(vy>V$})>_+cz^NzTcaV zye)2815jE$KG;~9NmHl=4CdGs|Jm4e@* zf9N5Y6k0(geDFg>2w}X8`hwswtj7jyL>GFb=v(m`1~H5gjG|!|640O*gr7zR7P2^u zBRGn8@IDrB94C;&Nqi)WaBlHds0}$glsgevjEIY+FS;FV17#As^OwYzSN?X}UVj6~ YZY~cpmve!HZO#9o`2Sgb?zL>ff6+-d{{R30 delta 1299 zcmd^<&u<$=6vy9pZO7wD(o7TE^|p3#Q#WnNj~eQPx`ZEz-%C};NWiWIKTs|G@ z7JOGs_yWRU8{uscJ;CKipg*PDVy4%mATzbOol1H1D zb?TveX_y{XpEytF=sCJbm#9jgsPnAT27OCE(=YTp{R4(Ah+ro=un+sug>LlW5Yo64 z{m9}7hH(@ln80yNVFo8K3lpa>2MZNASimBdaTd>F1?TZRUcf3|#A^Zrxd#bl9-j__tPelQwXXO$v`oh`@>(z`KZx~2-farS+B-Uvy@&7492ptgXx`EyIByHe(VY<)Z4LLS zObUIw9-UgWY%5gsm#Lxna+NfGWhq6IQVvqw|9)rYbpE$mPvKElB5w(n=bc zS~M%RRVr?ZZFjN2^la($X|w3qbxj`_WK68fpIU>Xx;`+(*a>?sS24$yie}|T$Wg{l zik6x_nzNm(VL7FW3yIL<`U8y3xW^ta40UL+EfO9-rt31>*6|;7T&54{ilXC7MaOsa zJzb?A=~w!buEPr-{EC#V*p3~DDq1>`La!oaP_Z$J`!S{nc?6H*Byx(566R5cjWddi zbBc@$ij0@=GG3|S@Xk0c<2_u#=UB%V*uW3Cifi}-*YOwr)*7@HtzAoKd$bO1ua?w$ swZ1wwntPqxHd7yN^x8cNoFPANjAe)T@J)0a_`h7OG^&nN|D^l=1^M70PXGV_ diff --git a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift index 8285d41d..7b3f6641 100644 --- a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift +++ b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift @@ -56,8 +56,12 @@ final actor VideoTracksManager { func observeLayerUpdates(for source: StreamSource) { Task { [weak self] in - guard let self, - await self.layerEventsObservationDictionary[source.sourceId] == nil else { return } + guard + let self, + await self.layerEventsObservationDictionary[source.sourceId] == nil + else { + return + } let layerEventsObservationTask = Task { for await layerEvent in source.videoTrack.layers() { let simulcastLayers = layerEvent.layers() diff --git a/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..e8c9ce62 --- /dev/null +++ b/interactive-player/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "fe0de64d75ed5bee65eefd076047e50c367be0fd662b2252f039523928594951", + "pins" : [ + { + "identity" : "millicast-sdk-swift-package", + "kind" : "remoteSourceControl", + "location" : "https://github.com/millicast/millicast-sdk-swift-package", + "state" : { + "revision" : "320331b070210fd41818e2e9459c2631991b6c16", + "version" : "2.0.0-beta.5" + } + } + ], + "version" : 3 +} diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 013389cea0d2946f5e0e23fda916bf25b1f8383e..38a8c449d0fb4826198e650be2bd2b1277dc00b0 100644 GIT binary patch delta 99 zcmZoMXfc=|#>B!ku~2NHo+2aX#(>?7iv?Ji7&#~NFj+7$B>^d|$puXPllhs{dCG!| z@^bR?(is>S7$@&z*4wPbyqsw>I|n}p(1^{9EZ>B)qu~2NHo+2a1#(>?7j2x4BSS*+r+klkT Date: Fri, 19 Jul 2024 10:05:46 -0600 Subject: [PATCH 14/19] minplayoutdelay fix --- .../StreamDetailInputScreen.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 1276b9a7..8a78929f 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -415,20 +415,17 @@ struct StreamDetailInputScreen: View { let disableAudio = false let videoQuality = VideoQuality.auto let saveLogs = false - let playoutDelayMin = showPlayoutDelay ? UInt(minPlayoutDelay) : nil - let playoutDelayMax = showPlayoutDelay ? UInt(maxPlayoutDelay) : nil - let maxBitrate: UInt = isShowingMaxBitrate ? UInt(maxBitrateString) ?? 0 : 0 - + RecentStreamCell(streamDetail: SavedStreamDetail( accountID: accountID, streamName: streamName, subscribeAPI: productionSubscribeURL, videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: playoutDelayMin, - maxPlayoutDelay: playoutDelayMax, + minPlayoutDelay: nil, + maxPlayoutDelay: nil, disableAudio: disableAudio, primaryVideoQuality: videoQuality, - maxBitrate: maxBitrate, + maxBitrate: 0, saveLogs: saveLogs )) { @@ -437,9 +434,9 @@ struct StreamDetailInputScreen: View { accountID: accountID, subscribeAPI: productionSubscribeURL, videoJitterMinimumDelayInMs: jitterMinimumDelayMs, - minPlayoutDelay: playoutDelayMin, - maxPlayoutDelay: playoutDelayMax, - maxBitrate: maxBitrate, + minPlayoutDelay: nil, + maxPlayoutDelay: nil, + maxBitrate: 0, disableAudio: disableAudio, primaryVideoQuality: videoQuality, saveLogs: saveLogs, @@ -456,7 +453,6 @@ struct StreamDetailInputScreen: View { let configuration = SubscriptionConfiguration( subscribeAPI: productionSubscribeURL, jitterMinimumDelayMs: jitterMinimumDelayMs, - maxBitrate: maxBitrate, disableAudio: disableAudio, rtcEventLogPath: nil, sdkLogPath: nil, From e6fc16c7255bf743c2c4f15b55ea5c17b3e0bbbf Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Fri, 19 Jul 2024 10:09:37 -0600 Subject: [PATCH 15/19] code identity --- .../Interactive Viewer/Managers/VideoTracksManager.swift | 4 ++-- .../StreamDetailInputScreen/StreamDetailInputScreen.swift | 2 +- interactive-player/RTSViewer.xcodeproj/project.pbxproj | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift index 7b3f6641..f3d75168 100644 --- a/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift +++ b/interactive-player/Interactive Viewer/Managers/VideoTracksManager.swift @@ -56,9 +56,9 @@ final actor VideoTracksManager { func observeLayerUpdates(for source: StreamSource) { Task { [weak self] in - guard + guard let self, - await self.layerEventsObservationDictionary[source.sourceId] == nil + await self.layerEventsObservationDictionary[source.sourceId] == nil else { return } diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 8a78929f..9136b2ed 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -415,7 +415,7 @@ struct StreamDetailInputScreen: View { let disableAudio = false let videoQuality = VideoQuality.auto let saveLogs = false - + RecentStreamCell(streamDetail: SavedStreamDetail( accountID: accountID, streamName: streamName, diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index cf0d7350..89164985 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -647,7 +647,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_BITCODE = NO; From e7187530ef0d70f9e392c64d0f883e0c8b861409 Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Fri, 19 Jul 2024 10:14:02 -0600 Subject: [PATCH 16/19] code signing --- interactive-player/RTSViewer.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/interactive-player/RTSViewer.xcodeproj/project.pbxproj b/interactive-player/RTSViewer.xcodeproj/project.pbxproj index 89164985..435c428b 100644 --- a/interactive-player/RTSViewer.xcodeproj/project.pbxproj +++ b/interactive-player/RTSViewer.xcodeproj/project.pbxproj @@ -712,7 +712,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_BITCODE = NO; @@ -833,7 +832,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_BITCODE = NO; From a61223eefb840b977f3e456f6fe257bee141b7bd Mon Sep 17 00:00:00 2001 From: Sheila Doherty Date: Fri, 19 Jul 2024 12:35:06 -0600 Subject: [PATCH 17/19] revert to 15.2 --- .DS_Store | Bin 10244 -> 10244 bytes .../xcshareddata/swiftpm/Package.resolved | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.DS_Store b/.DS_Store index 54907672e704e63d9201d7ad3b43413c6a5276dd..2063782bf9c364d2b6b2b9b050ac5f5e876e8217 100644 GIT binary patch delta 29 kcmZn(XbIS`MS#=D!rWX(!OYlV^Im}>c1DKH1`=2J0gLJg!vFvP delta 29 kcmZn(XbIS`MS#=T*wS1_!OX&V^Im}>c1Fg{1`=2J0gKEC!T Date: Fri, 19 Jul 2024 14:23:44 -0600 Subject: [PATCH 18/19] more clean up --- .DS_Store | Bin 10244 -> 10244 bytes .../Models/SubscriptionConfiguration.swift | 4 +- .../Models/SavedStreamDetail.swift | 37 ++++++++++-------- .../RecentStreams/RecentStreamCell.swift | 3 +- .../RecentStreamsViewModel.swift | 11 ++++-- .../StreamDetailInputScreen.swift | 4 +- rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes .../xcshareddata/swiftpm/Package.resolved | 24 ------------ 8 files changed, 31 insertions(+), 52 deletions(-) delete mode 100644 rts-viewer-ios/RTSViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.DS_Store b/.DS_Store index 2063782bf9c364d2b6b2b9b050ac5f5e876e8217..d70680715956692cb7267548da9e98141ea784a0 100644 GIT binary patch delta 78 zcmZn(XbIS$Dagn$IZ#Aqx^1H}t@B&(~9j1A0n6ikhbC+`)opKL54yIEc0 OF8gM71y6SDip&6|-4^Bm delta 94 zcmZn(XbIS$Dagn;IZ#Ap0U1H}tD#j2|fbQDZ1%_pA|muBpk{7PJ&wUdE? cfp79fdGW~(;;fssB<`|rW>N5A$EiaW07>o~MF0Q* diff --git a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift index 21eb50e3..8abf57e8 100644 --- a/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift +++ b/LocalPackages/RTSCore/Sources/RTSCore/Models/SubscriptionConfiguration.swift @@ -39,8 +39,8 @@ public struct SubscriptionConfiguration { rtcEventLogPath: String? = nil, sdkLogPath: String? = nil, enableStats: Bool = Constants.enableStats, - playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay) - { + playoutDelay: MCForcePlayoutDelay? = Constants.playoutDelay + ) { self.subscribeAPI = subscribeAPI self.autoReconnect = autoReconnect self.jitterMinimumDelayMs = jitterMinimumDelayMs diff --git a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift index c8522815..455c97c9 100644 --- a/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift +++ b/interactive-player/Interactive Viewer/Models/SavedStreamDetail.swift @@ -18,17 +18,19 @@ struct SavedStreamDetail: Identifiable, Equatable { let maxBitrate: UInt? let saveLogs: Bool - init(accountID: String, - streamName: String, - subscribeAPI: String, - videoJitterMinimumDelayInMs: UInt, - minPlayoutDelay: UInt?, - maxPlayoutDelay: UInt?, - disableAudio: Bool, - primaryVideoQuality: VideoQuality, - maxBitrate: UInt, - saveLogs: Bool, - dateProvider: DateProvider = DefaultDateProvider()) { + init( + accountID: String, + streamName: String, + subscribeAPI: String, + videoJitterMinimumDelayInMs: UInt, + minPlayoutDelay: UInt?, + maxPlayoutDelay: UInt?, + disableAudio: Bool, + primaryVideoQuality: VideoQuality, + maxBitrate: UInt, + saveLogs: Bool, + dateProvider: DateProvider = DefaultDateProvider() + ) { self.id = UUID() self.accountID = accountID self.streamName = streamName @@ -46,12 +48,13 @@ struct SavedStreamDetail: Identifiable, Equatable { extension SavedStreamDetail { init?(managedObject: StreamDetailManagedObject) { - guard let accountID = managedObject.accountID, - let streamName = managedObject.streamName, - let lastUsedDate = managedObject.lastUsedDate, - let storedVideoQuality = managedObject.primaryVideoQuality, - let subscribeAPI = managedObject.subscribeAPI, - let primaryVideoQuality = VideoQuality(rawValue: storedVideoQuality) + guard + let accountID = managedObject.accountID, + let streamName = managedObject.streamName, + let lastUsedDate = managedObject.lastUsedDate, + let storedVideoQuality = managedObject.primaryVideoQuality, + let subscribeAPI = managedObject.subscribeAPI, + let primaryVideoQuality = VideoQuality(rawValue: storedVideoQuality) else { return nil } diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift index 96252baf..99c9562c 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamCell.swift @@ -39,8 +39,7 @@ struct RecentStreamCell: View { private let action: () -> Void - init(streamDetail: SavedStreamDetail, - action: @escaping () -> Void) { + init(streamDetail: SavedStreamDetail, action: @escaping () -> Void) { self.streamDetail = streamDetail self.action = action } diff --git a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift index ea02dcef..435af69d 100644 --- a/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/RecentStreams/RecentStreamsViewModel.swift @@ -24,9 +24,11 @@ final class RecentStreamsViewModel: ObservableObject { @Published private(set) var topStreamDetails: [SavedStreamDetail] = [] @Published private(set) var lastPlayedStream: SavedStreamDetail? - init(streamDataManager: StreamDataManagerProtocol = StreamDataManager.shared, - settingsManager: SettingsManager = .shared, - dateProvider: DateProvider = DefaultDateProvider()) { + init( + streamDataManager: StreamDataManagerProtocol = StreamDataManager.shared, + settingsManager: SettingsManager = .shared, + dateProvider: DateProvider = DefaultDateProvider() + ) { self.streamDataManager = streamDataManager self.settingsManager = settingsManager self.dateProvider = dateProvider @@ -90,7 +92,8 @@ final class RecentStreamsViewModel: ObservableObject { let sdkLogPath = streamDetail.saveLogs ? URL.sdkLogPath(for: currentDate) : nil var playoutDelay: MCForcePlayoutDelay? if let minPlayoutDelay = streamDetail.minPlayoutDelay, - let maxPlayoutDelay = streamDetail.maxPlayoutDelay { + let maxPlayoutDelay = streamDetail.maxPlayoutDelay + { playoutDelay = MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) } diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift index 9136b2ed..64eda756 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputScreen.swift @@ -448,15 +448,13 @@ struct StreamDetailInputScreen: View { return } - let playoutDelay: MCForcePlayoutDelay? = showPlayoutDelay ? MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) : nil - let configuration = SubscriptionConfiguration( subscribeAPI: productionSubscribeURL, jitterMinimumDelayMs: jitterMinimumDelayMs, disableAudio: disableAudio, rtcEventLogPath: nil, sdkLogPath: nil, - playoutDelay: playoutDelay + playoutDelay: nil ) streamingScreenContext = StreamingView.Context( streamName: streamName, diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 38a8c449d0fb4826198e650be2bd2b1277dc00b0..6ef4f1d0a423e3a6a4fdf61bc4f2a9b826e5632d 100644 GIT binary patch delta 44 zcmZoMXffE}%)-p@f5T)R7OlwzEM1HSlYg@mGx5%tT)=9@W-?tRqEU782G$TE07)qh APyhe` delta 41 xcmZoMXffE}%)-pblr))#MQd^aOBbWV Date: Fri, 19 Jul 2024 14:36:56 -0600 Subject: [PATCH 19/19] cleanup --- .DS_Store | Bin 10244 -> 10244 bytes .../StreamDetailInputViewModel.swift | 3 +-- rts-viewer-ios/.DS_Store | Bin 6148 -> 6148 bytes rts-viewer-tvos/.DS_Store | Bin 6148 -> 6148 bytes 4 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.DS_Store b/.DS_Store index d70680715956692cb7267548da9e98141ea784a0..5037a2d3b10cb0d079725a066df5e54e3e2123e3 100644 GIT binary patch delta 36 scmZn(XbIS$Dagn;IZ#A`{V%ey_3Jl$Zt-UoX@tIUEwb~0NWG{J^%m! delta 62 zcmZn(XbIS$Dagn$IZ#AJL8VYf#QYS64lkF=4LtyCYI)t&xuPjc1?aIF3;FK SxlmGRbB5%6w#{q`|JVUXjuSBe diff --git a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift index b0dc632e..bf20bd07 100644 --- a/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift +++ b/interactive-player/Interactive Viewer/Views/StreamDetailInputScreen/StreamDetailInputViewModel.swift @@ -84,8 +84,7 @@ final class StreamDetailInputViewModel: ObservableObject { saveLogs: Bool ) -> SubscriptionConfiguration { var playoutDelay: MCForcePlayoutDelay? - if let minPlayoutDelay, - let maxPlayoutDelay { + if let minPlayoutDelay, let maxPlayoutDelay { playoutDelay = MCForcePlayoutDelay(min: Int32(minPlayoutDelay), max: Int32(maxPlayoutDelay)) } let currentDate = dateProvider.now diff --git a/rts-viewer-ios/.DS_Store b/rts-viewer-ios/.DS_Store index 6ef4f1d0a423e3a6a4fdf61bc4f2a9b826e5632d..6dd2aae579a9a36544189291aa5367bff32ee811 100644 GIT binary patch delta 55 zcmZoMXffE3%gW5~$9-}Es}-x#iN{e*lh?D_veXNFji0=Z)rh|!!!S5GKevDZ1Q-K0 J8?s#%1OOc-5#0a) delta 60 zcmZoMXffE3%gW5iJ7aPIs}+mMbd`w7A6ShhKVaoyd-M6f&wG{08(0na3o;CYlk;;6 O7(jqAVY4CIWkCRXAQS@t diff --git a/rts-viewer-tvos/.DS_Store b/rts-viewer-tvos/.DS_Store index bba5921baa5c2595f9f7322b8ae9bfd56e24bc04..60990a88d5d4c13e64dd921940e779f4ea17711a 100644 GIT binary patch delta 82 zcmZoMXffDO!^Czy>00YsmB|N~bSAH3;$hPho+We{#I0aj{c-X>X1&Q7%sgzI-vpR| hqI;MPCa(f=FF#uQ_yUM)$0)E_kb{S1Gdss$egGov9$5eY delta 80 zcmZoMXffDO!^Adqear(f@yQ36bSAH3;z4jLm=;UgXx