expo-realtime-ivs-broadcast 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/ios/ExpoIVSRemoteStreamView.swift +11 -3
- package/ios/IVSStageManager.swift +43 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,8 @@ This module provides React Native components and a comprehensive API to integrat
|
|
|
8
8
|
|
|
9
9
|
| Library Version | Expo SDK | React Native | React | Notes |
|
|
10
10
|
|-----------------|----------|--------------|---------|-------|
|
|
11
|
-
| 0.2.
|
|
11
|
+
| 0.2.1 | 54 | 0.81.x | 19.1.x | **Fixed iOS PiP pre-warming reliability** |
|
|
12
|
+
| 0.2.0 | 54 | 0.81.x | 19.1.x | Added Picture-in-Picture support |
|
|
12
13
|
| 0.1.7 | 54 | 0.81.x | 19.1.x | |
|
|
13
14
|
| 0.1.4 | 53 | 0.79.x | 19.0.x | |
|
|
14
15
|
|
|
@@ -103,9 +103,17 @@ class ExpoIVSRemoteStreamView: ExpoView {
|
|
|
103
103
|
print("✅ [REMOTE VIEW] Manager commanded me to render URN: \(deviceUrn)")
|
|
104
104
|
|
|
105
105
|
// Notify the stage manager that a stream started rendering (for PiP)
|
|
106
|
-
// Use a
|
|
107
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.
|
|
108
|
-
self
|
|
106
|
+
// Use a longer delay to ensure the view hierarchy is fully set up
|
|
107
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
|
|
108
|
+
guard let self = self else { return }
|
|
109
|
+
print("✅ [REMOTE VIEW] Notifying PiP system - view in window: \(self.window != nil), isRenderingVideo: \(self.isRenderingVideo)")
|
|
110
|
+
self.stageManager?.notifyRemoteStreamRendered()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Also notify immediately for faster setup if view is already ready
|
|
114
|
+
if self.window != nil {
|
|
115
|
+
print("✅ [REMOTE VIEW] View already in window, notifying PiP immediately")
|
|
116
|
+
self.stageManager?.notifyRemoteStreamRendered()
|
|
109
117
|
}
|
|
110
118
|
} catch {
|
|
111
119
|
print("❌ [REMOTE VIEW] Failed to create preview for URN \(deviceUrn): \(error)")
|
|
@@ -1057,13 +1057,27 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1057
1057
|
let previewView = try device.previewView()
|
|
1058
1058
|
pipController.setupWithSourceView(previewView)
|
|
1059
1059
|
pipTargetView = previewView
|
|
1060
|
+
print("🖼️ [PiP] Set up with device preview view")
|
|
1060
1061
|
} catch {
|
|
1061
1062
|
print("🖼️ [PiP] Warning: Could not get preview view from device: \(error)")
|
|
1062
1063
|
// Try to find a remote view that's rendering this device
|
|
1063
|
-
if let remoteView = remoteViews.compactMap({ $0.value }).first(where: { $0.currentRenderedDeviceUrn == device.descriptor().urn })
|
|
1064
|
-
|
|
1064
|
+
if let remoteView = remoteViews.compactMap({ $0.value }).first(where: { $0.currentRenderedDeviceUrn == device.descriptor().urn }) {
|
|
1065
|
+
// Always set up with the remote view container - it's the visible video view
|
|
1065
1066
|
pipController.setupWithSourceView(remoteView as UIView)
|
|
1066
|
-
pipTargetView = previewViewForPiP
|
|
1067
|
+
pipTargetView = remoteView.previewViewForPiP ?? remoteView
|
|
1068
|
+
print("🖼️ [PiP] Set up with matching remote view container")
|
|
1069
|
+
} else if let anyRenderingView = remoteViews.compactMap({ $0.value }).first(where: { $0.isRenderingVideo }) {
|
|
1070
|
+
// Fallback: use any view that's rendering
|
|
1071
|
+
pipController.setupWithSourceView(anyRenderingView as UIView)
|
|
1072
|
+
pipTargetView = anyRenderingView.previewViewForPiP ?? anyRenderingView
|
|
1073
|
+
print("🖼️ [PiP] Set up with fallback rendering view")
|
|
1074
|
+
} else if let anyView = remoteViews.compactMap({ $0.value }).first {
|
|
1075
|
+
// Last resort: use any registered view
|
|
1076
|
+
pipController.setupWithSourceView(anyView as UIView)
|
|
1077
|
+
pipTargetView = anyView
|
|
1078
|
+
print("🖼️ [PiP] Set up with any available view (last resort)")
|
|
1079
|
+
} else {
|
|
1080
|
+
print("🖼️ [PiP] ERROR: No source view found for PiP - controller will NOT be initialized!")
|
|
1067
1081
|
}
|
|
1068
1082
|
}
|
|
1069
1083
|
}
|
|
@@ -1111,20 +1125,43 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1111
1125
|
if currentPiPSourceDeviceUrn != imageDevice.descriptor().urn {
|
|
1112
1126
|
print("🖼️ [PiP] Found new remote video stream: \(imageDevice.descriptor().urn)")
|
|
1113
1127
|
|
|
1128
|
+
// Debug: Log all registered remote views and their URNs
|
|
1129
|
+
print("🖼️ [PiP] Registered remote views: \(remoteViews.count)")
|
|
1130
|
+
for (index, viewWrapper) in remoteViews.enumerated() {
|
|
1131
|
+
if let view = viewWrapper.value {
|
|
1132
|
+
print("🖼️ [PiP] View \(index): URN=\(view.currentRenderedDeviceUrn ?? "nil"), isRendering=\(view.isRenderingVideo)")
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1114
1136
|
// Try to find the remote view that's rendering this stream
|
|
1115
1137
|
if let remoteView = remoteViews.compactMap({ $0.value }).first(where: { $0.currentRenderedDeviceUrn == imageDevice.descriptor().urn }) {
|
|
1116
|
-
// Use the remote view (container) as the source view for Video Call API
|
|
1117
|
-
// Cast to UIView explicitly since ExpoIVSRemoteStreamView extends ExpoView -> UIView
|
|
1118
1138
|
candidateSourceView = remoteView as UIView
|
|
1119
1139
|
print("🖼️ [PiP] Found matching remote view for source")
|
|
1140
|
+
} else {
|
|
1141
|
+
// Fallback: Use ANY remote view that's rendering video
|
|
1142
|
+
if let anyRenderingView = remoteViews.compactMap({ $0.value }).first(where: { $0.isRenderingVideo }) {
|
|
1143
|
+
candidateSourceView = anyRenderingView as UIView
|
|
1144
|
+
print("🖼️ [PiP] Using fallback remote view (URN didn't match but view is rendering)")
|
|
1145
|
+
} else if let anyView = remoteViews.compactMap({ $0.value }).first {
|
|
1146
|
+
// Last resort: use any registered view
|
|
1147
|
+
candidateSourceView = anyView as UIView
|
|
1148
|
+
print("🖼️ [PiP] Using any available remote view as last resort")
|
|
1149
|
+
}
|
|
1120
1150
|
}
|
|
1121
1151
|
|
|
1122
1152
|
attachToDevice(imageDevice, sourceView: candidateSourceView)
|
|
1123
1153
|
}
|
|
1124
1154
|
} else {
|
|
1125
|
-
// No candidate stream found
|
|
1155
|
+
// No candidate stream found - log why
|
|
1126
1156
|
if currentPiPDevice == nil {
|
|
1127
1157
|
print("🖼️ [PiP] No active remote video stream found yet")
|
|
1158
|
+
print("🖼️ [PiP] Total participants: \(participants.count)")
|
|
1159
|
+
for p in participants {
|
|
1160
|
+
print("🖼️ [PiP] Participant \(p.info.participantId ?? "nil"): \(p.streams.count) streams")
|
|
1161
|
+
for s in p.streams {
|
|
1162
|
+
print("🖼️ [PiP] Stream type: \(s.device.descriptor().type.rawValue), URN: \(s.device.descriptor().urn)")
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1128
1165
|
}
|
|
1129
1166
|
}
|
|
1130
1167
|
}
|