Skip to content

Commit

Permalink
Layer selection logic for iOS and tvOS apps
Browse files Browse the repository at this point in the history
Add 1.7.0 SDK on RTS Interactive Viewer iOS App
Add 1.7.0SDK on RTS Interactive Viewer tvOS App
  • Loading branch information
aravind-raveendran committed Feb 13, 2024
1 parent a392fad commit 9332cf2
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd_rts_viewer_ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: RTS Viewer Build and Distribution
on:
push:
branches:
- 'main'
- 'bugfix/rts-viewer-tv-n-ios'
paths:
- 'rts-viewer-ios/**'
tags:
Expand Down
4 changes: 2 additions & 2 deletions rts-viewer-ios/LocalPackages/RTSComponentKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/millicast/millicast-sdk-swift-package", from: "1.5.0"),
.package(url: "https://github.com/DolbyIO/rts-uikit-ios", from: "0.0.4")
.package(url: "https://github.com/millicast/millicast-sdk-swift-package", exact: "1.7.0"),
.package(url: "https://github.com/DolbyIO/rts-uikit-ios", exact: "0.0.4")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public protocol SubscriptionManagerDelegate: AnyObject {
func onStreamInactive()
func onStreamStopped()
func onConnectionError(reason: String)
func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [MCLayerData]?)
func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [String]?)
}

public protocol SubscriptionManagerProtocol: AnyObject {
Expand Down Expand Up @@ -176,32 +176,36 @@ private extension SubscriptionManager {

extension SubscriptionManager: MCSubscriberListener {

public func onDisconnected() {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onDisconnected()")
}

public func onSubscribed() {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSubscribed()")
delegate?.onSubscribed()
}

public func onSubscribedError(_ reason: String!) {
public func onSubscribedError(_ reason: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSubscribedError(_ reason:)")
delegate?.onSubscribedError(reason)
}

public func onVideoTrack(_ track: MCVideoTrack!, withMid mid: String!) {
public func onVideoTrack(_ track: MCVideoTrack, withMid mid: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onVideoTrack(_ mid:)")
delegate?.onVideoTrack(track, withMid: mid)
}

public func onAudioTrack(_ track: MCAudioTrack!, withMid mid: String!) {
public func onAudioTrack(_ track: MCAudioTrack, withMid mid: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onAudioTrack(_ mid:)")
delegate?.onAudioTrack(track, withMid: mid)
}

public func onActive(_ streamId: String!, tracks: [String]!, sourceId: String!) {
public func onActive(_ streamId: String, tracks: [String], sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onActive(_ streamId:tracks:sourceId:)")
delegate?.onStreamActive()
}

public func onInactive(_ streamId: String!, sourceId: String!) {
public func onInactive(_ streamId: String, sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onInactive(_ streamId:sourceId:)")
delegate?.onStreamInactive()
}
Expand All @@ -211,11 +215,11 @@ extension SubscriptionManager: MCSubscriberListener {
delegate?.onStreamStopped()
}

public func onVad(_ mid: String!, sourceId: String!) {
public func onVad(_ mid: String, sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onVad(_ mid:sourceId:)")
}

public func onLayers(_ mid: String!, activeLayers: [MCLayerData]!, inactiveLayers: [MCLayerData]!) {
public func onLayers(_ mid: String, activeLayers: [MCLayerData], inactiveLayers: [String]) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onLayers(_ mid:activeLayers:inactiveLayers:)")
delegate?.onStreamLayers(mid, activeLayers: activeLayers, inactiveLayers: inactiveLayers)
}
Expand All @@ -225,16 +229,16 @@ extension SubscriptionManager: MCSubscriberListener {
delegate?.onConnected()
}

public func onConnectionError(_ status: Int32, withReason reason: String!) {
public func onConnectionError(_ status: Int32, withReason reason: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onConnectionError(_ status:withReason:)")
delegate?.onConnectionError(reason: reason)
}

public func onSignalingError(_ message: String!) {
public func onSignalingError(_ message: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSignalingError(_ message:)")
}

public func onStatsReport(_ report: MCStatsReport!) {
public func onStatsReport(_ report: MCStatsReport) {
delegate?.onStatsReport(report: report)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,35 @@ extension RTSDataStore: SubscriptionManagerDelegate {
updateState(to: .error(.connectError(reason: reason)))
}

public func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [MCLayerData]?) {
public func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [String]?) {
Task {
await MainActor.run {
layerActiveMap = activeLayers?.filter { layer in
// For H.264 there are no temporal layers and the id is set to 255. For VP8 use the first temporal layer.
return layer.temporalLayerId == 0 || layer.temporalLayerId == 255
var layersForSelection: [MCLayerData] = []

// Simulcast active layers
if let simulcastLayers = activeLayers?.filter({ !$0.encodingId.isEmpty }), !simulcastLayers.isEmpty {
// Select the max (best) temporal layer Id from a specific encodingId
let dictionaryOfLayersMatchingEncodingId = Dictionary(grouping: simulcastLayers, by: { $0.encodingId })
dictionaryOfLayersMatchingEncodingId.forEach { (_: String, layers: [MCLayerData]) in
// Picking the layer matching the max temporal layer id - represents the layer with the best FPS
if let layerWithBestFrameRate = layers.first(where: { $0.temporalLayerId == $0.maxTemporalLayerId }) ?? layers.last {
layersForSelection.append(layerWithBestFrameRate)
}
}
layersForSelection.sort(by: >)
}
// Using SVC layer selection logic
else if let simulcastLayers = activeLayers?.filter({ $0.spatialLayerId != nil }) {
let dictionaryOfLayersMatchingSpatialLayerId = Dictionary(grouping: simulcastLayers, by: { $0.spatialLayerId! })
dictionaryOfLayersMatchingSpatialLayerId.forEach { (_: NSNumber, layers: [MCLayerData]) in
// Picking the layer matching the max temporal layer id - represents the layer with the best FPS
if let layerWithBestFrameRate = layers.first(where: { $0.spatialLayerId == $0.maxSpatialLayerId }) ?? layers.last {
layersForSelection.append(layerWithBestFrameRate)
}
}
}

layerActiveMap = layersForSelection
activeStreamType.removeAll()

switch layerActiveMap?.count {
Expand Down Expand Up @@ -341,3 +362,14 @@ extension RTSDataStore: SubscriptionManagerDelegate {
return codecStats.mime_type as String
}
}

extension MCLayerData: Comparable {
public static func < (lhs: MCLayerData, rhs: MCLayerData) -> Bool {
switch (lhs.encodingId.lowercased(), rhs.encodingId.lowercased()) {
case ("h", "m"), ("l", "m"), ("h", "s"), ("l", "s"), ("m", "s"):
return false
default:
return true
}
}
}
9 changes: 0 additions & 9 deletions rts-viewer-ios/RTSViewer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,6 @@
);
mainGroup = 3FE90C0F26B2AF4200B206A3;
packageReferences = (
E8575E9729527B420070F53E /* XCRemoteSwiftPackageReference "millicast-sdk-swift-package" */,
B63736D32A3958BD0004C4EF /* XCRemoteSwiftPackageReference "rts-uikit-ios" */,
);
productRefGroup = 3FE90C1926B2AF4200B206A3 /* Products */;
Expand Down Expand Up @@ -862,14 +861,6 @@
version = 0.0.4;
};
};
E8575E9729527B420070F53E /* XCRemoteSwiftPackageReference "millicast-sdk-swift-package" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/millicast/millicast-sdk-swift-package";
requirement = {
kind = exactVersion;
version = 1.5.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/millicast/millicast-sdk-swift-package",
"state" : {
"revision" : "f5e0abd63564f591d4fef0ab82d9e0cdee858579",
"version" : "1.5.0"
"revision" : "001f8654ba31461ecf805ae8a15e4d92efa8064e",
"version" : "1.7.0"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions rts-viewer-tvos/LocalPackages/RTSComponentKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/millicast/millicast-sdk-swift-package", from: "1.5.0"),
.package(url: "https://github.com/DolbyIO/rts-uikit-ios", branch: "0.0.4")
.package(url: "https://github.com/millicast/millicast-sdk-swift-package", from: "1.7.0"),
.package(url: "https://github.com/DolbyIO/rts-uikit-ios", exact: "0.0.4")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public protocol SubscriptionManagerDelegate: AnyObject {
func onStreamInactive()
func onStreamStopped()
func onConnectionError(reason: String)
func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [MCLayerData]?)
func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [String]?)
}

public protocol SubscriptionManagerProtocol: AnyObject {
Expand Down Expand Up @@ -175,33 +175,36 @@ private extension SubscriptionManager {
}

extension SubscriptionManager: MCSubscriberListener {
public func onDisconnected() {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onDisconnected()")
}

public func onSubscribed() {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSubscribed()")
delegate?.onSubscribed()
}

public func onSubscribedError(_ reason: String!) {
public func onSubscribedError(_ reason: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSubscribedError(_ reason:)")
delegate?.onSubscribedError(reason)
}

public func onVideoTrack(_ track: MCVideoTrack!, withMid mid: String!) {
public func onVideoTrack(_ track: MCVideoTrack, withMid mid: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onVideoTrack(_ mid:)")
delegate?.onVideoTrack(track, withMid: mid)
}

public func onAudioTrack(_ track: MCAudioTrack!, withMid mid: String!) {
public func onAudioTrack(_ track: MCAudioTrack, withMid mid: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onAudioTrack(_ mid:)")
delegate?.onAudioTrack(track, withMid: mid)
}

public func onActive(_ streamId: String!, tracks: [String]!, sourceId: String!) {
public func onActive(_ streamId: String, tracks: [String], sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onActive(_ streamId:tracks:sourceId:)")
delegate?.onStreamActive()
}

public func onInactive(_ streamId: String!, sourceId: String!) {
public func onInactive(_ streamId: String, sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onInactive(_ streamId:sourceId:)")
delegate?.onStreamInactive()
}
Expand All @@ -211,11 +214,11 @@ extension SubscriptionManager: MCSubscriberListener {
delegate?.onStreamStopped()
}

public func onVad(_ mid: String!, sourceId: String!) {
public func onVad(_ mid: String, sourceId: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onVad(_ mid:sourceId:)")
}

public func onLayers(_ mid: String!, activeLayers: [MCLayerData]!, inactiveLayers: [MCLayerData]!) {
public func onLayers(_ mid: String, activeLayers: [MCLayerData], inactiveLayers: [String]) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onLayers(_ mid:activeLayers:inactiveLayers:)")
delegate?.onStreamLayers(mid, activeLayers: activeLayers, inactiveLayers: inactiveLayers)
}
Expand All @@ -225,16 +228,16 @@ extension SubscriptionManager: MCSubscriberListener {
delegate?.onConnected()
}

public func onConnectionError(_ status: Int32, withReason reason: String!) {
public func onConnectionError(_ status: Int32, withReason reason: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onConnectionError(_ status:withReason:)")
delegate?.onConnectionError(reason: reason)
}

public func onSignalingError(_ message: String!) {
public func onSignalingError(_ message: String) {
Self.logger.debug("Delegate - \(MCSubscriberListener.self) - onSignalingError(_ message:)")
}

public func onStatsReport(_ report: MCStatsReport!) {
public func onStatsReport(_ report: MCStatsReport) {
delegate?.onStatsReport(report: report)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,35 @@ extension RTSDataStore: SubscriptionManagerDelegate {
updateState(to: .error(.connectError(reason: reason)))
}

public func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [MCLayerData]?) {
public func onStreamLayers(_ mid: String?, activeLayers: [MCLayerData]?, inactiveLayers: [String]?) {
Task {
await MainActor.run {
layerActiveMap = activeLayers?.filter { layer in
// For H.264 there are no temporal layers and the id is set to 255. For VP8 use the first temporal layer.
return layer.temporalLayerId == 0 || layer.temporalLayerId == 255
var layersForSelection: [MCLayerData] = []

// Simulcast active layers
if let simulcastLayers = activeLayers?.filter({ !$0.encodingId.isEmpty }), !simulcastLayers.isEmpty {
// Select the max (best) temporal layer Id from a specific encodingId
let dictionaryOfLayersMatchingEncodingId = Dictionary(grouping: simulcastLayers, by: { $0.encodingId })
dictionaryOfLayersMatchingEncodingId.forEach { (_: String, layers: [MCLayerData]) in
// Picking the layer matching the max temporal layer id - represents the layer with the best FPS
if let layerWithBestFrameRate = layers.first(where: { $0.temporalLayerId == $0.maxTemporalLayerId }) ?? layers.last {
layersForSelection.append(layerWithBestFrameRate)
}
}
layersForSelection.sort(by: >)
}
// Using SVC layer selection logic
else if let simulcastLayers = activeLayers?.filter({ $0.spatialLayerId != nil }) {
let dictionaryOfLayersMatchingSpatialLayerId = Dictionary(grouping: simulcastLayers, by: { $0.spatialLayerId! })
dictionaryOfLayersMatchingSpatialLayerId.forEach { (_: NSNumber, layers: [MCLayerData]) in
// Picking the layer matching the max temporal layer id - represents the layer with the best FPS
if let layerWithBestFrameRate = layers.first(where: { $0.spatialLayerId == $0.maxSpatialLayerId }) ?? layers.last {
layersForSelection.append(layerWithBestFrameRate)
}
}
}

layerActiveMap = layersForSelection

activeStreamType.removeAll()

Expand Down Expand Up @@ -341,3 +363,14 @@ extension RTSDataStore: SubscriptionManagerDelegate {
return codecStats.mime_type as String
}
}

extension MCLayerData: Comparable {
public static func < (lhs: MCLayerData, rhs: MCLayerData) -> Bool {
switch (lhs.encodingId.lowercased(), rhs.encodingId.lowercased()) {
case ("h", "m"), ("l", "m"), ("h", "s"), ("l", "s"), ("m", "s"):
return false
default:
return true
}
}
}
9 changes: 0 additions & 9 deletions rts-viewer-tvos/RTSViewer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,6 @@
);
mainGroup = 3FE90C0F26B2AF4200B206A3;
packageReferences = (
E8575E9729527B420070F53E /* XCRemoteSwiftPackageReference "millicast-sdk-swift-package" */,
B63736D32A3958BD0004C4EF /* XCRemoteSwiftPackageReference "rts-uikit-ios" */,
);
productRefGroup = 3FE90C1926B2AF4200B206A3 /* Products */;
Expand Down Expand Up @@ -1216,14 +1215,6 @@
version = 0.0.4;
};
};
E8575E9729527B420070F53E /* XCRemoteSwiftPackageReference "millicast-sdk-swift-package" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/millicast/millicast-sdk-swift-package";
requirement = {
kind = exactVersion;
version = 1.5.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/millicast/millicast-sdk-swift-package",
"state" : {
"revision" : "f5e0abd63564f591d4fef0ab82d9e0cdee858579",
"version" : "1.5.0"
"revision" : "08eba00370296c1720ce37f9343f02cb0bb40813",
"version" : "1.7.1"
}
},
{
"identity" : "rts-uikit-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/DolbyIO/rts-uikit-ios",
"state" : {
"branch" : "0.0.4",
"revision" : "e5a2a8150dcc79a14d13a8df2ecdf15c7a6e5794"
"revision" : "e5a2a8150dcc79a14d13a8df2ecdf15c7a6e5794",
"version" : "0.0.4"
}
}
],
Expand Down

0 comments on commit 9332cf2

Please sign in to comment.