expo-realtime-ivs-broadcast 0.2.3 → 0.2.4
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/ios/IVSStageManager.swift +52 -34
- package/package.json +1 -1
|
@@ -304,6 +304,8 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
304
304
|
private var currentPiPDevice: IVSImageDevice?
|
|
305
305
|
// Store the local preview view for broadcaster PiP
|
|
306
306
|
private weak var localPreviewView: UIView?
|
|
307
|
+
// Track whether we have a valid visible source view (not just device.previewView())
|
|
308
|
+
private var pipHasValidSourceView: Bool = false
|
|
307
309
|
|
|
308
310
|
@available(iOS 15.0, *)
|
|
309
311
|
private var pipController: IVSPictureInPictureController {
|
|
@@ -1026,19 +1028,22 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1026
1028
|
currentPiPDevice = nil
|
|
1027
1029
|
currentPiPSourceDeviceUrn = nil
|
|
1028
1030
|
pipTargetView = nil
|
|
1031
|
+
pipHasValidSourceView = false
|
|
1029
1032
|
}
|
|
1030
1033
|
|
|
1031
1034
|
/// Attach frame callback to an IVS Image Device
|
|
1032
1035
|
@available(iOS 15.0, *)
|
|
1033
1036
|
private func attachToDevice(_ device: IVSImageDevice, sourceView: UIView? = nil) {
|
|
1034
|
-
// If we are already attached to this device, do nothing
|
|
1035
|
-
if currentPiPDevice === device {
|
|
1037
|
+
// If we are already attached to this device AND have a valid source view, do nothing
|
|
1038
|
+
if currentPiPDevice === device && pipHasValidSourceView {
|
|
1039
|
+
print("🖼️ [PiP] Already attached to device with valid source view, skipping")
|
|
1036
1040
|
return
|
|
1037
1041
|
}
|
|
1038
1042
|
|
|
1039
1043
|
// If attached to another device, detach first
|
|
1040
|
-
if let oldDevice = currentPiPDevice {
|
|
1044
|
+
if let oldDevice = currentPiPDevice, oldDevice !== device {
|
|
1041
1045
|
oldDevice.setOnFrameCallback(nil)
|
|
1046
|
+
print("🖼️ [PiP] Detached from previous device")
|
|
1042
1047
|
}
|
|
1043
1048
|
|
|
1044
1049
|
currentPiPDevice = device
|
|
@@ -1051,39 +1056,44 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1051
1056
|
if let view = sourceView {
|
|
1052
1057
|
pipController.setupWithSourceView(view)
|
|
1053
1058
|
pipTargetView = view
|
|
1059
|
+
pipHasValidSourceView = true
|
|
1060
|
+
print("🖼️ [PiP] Set up with provided source view (VALID)")
|
|
1054
1061
|
} else {
|
|
1055
|
-
//
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
pipController.setupWithSourceView(
|
|
1059
|
-
pipTargetView =
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1062
|
+
// For remote streams, device.previewView() returns an internal view that's not visible
|
|
1063
|
+
// We should prefer finding a registered remote view first
|
|
1064
|
+
if let remoteView = remoteViews.compactMap({ $0.value }).first(where: { $0.currentRenderedDeviceUrn == device.descriptor().urn }) {
|
|
1065
|
+
pipController.setupWithSourceView(remoteView as UIView)
|
|
1066
|
+
pipTargetView = remoteView.previewViewForPiP ?? remoteView
|
|
1067
|
+
pipHasValidSourceView = true
|
|
1068
|
+
print("🖼️ [PiP] Set up with matching remote view container (VALID)")
|
|
1069
|
+
} else if let anyRenderingView = remoteViews.compactMap({ $0.value }).first(where: { $0.isRenderingVideo }) {
|
|
1070
|
+
pipController.setupWithSourceView(anyRenderingView as UIView)
|
|
1071
|
+
pipTargetView = anyRenderingView.previewViewForPiP ?? anyRenderingView
|
|
1072
|
+
pipHasValidSourceView = true
|
|
1073
|
+
print("🖼️ [PiP] Set up with fallback rendering view (VALID)")
|
|
1074
|
+
} else if let anyView = remoteViews.compactMap({ $0.value }).first {
|
|
1075
|
+
pipController.setupWithSourceView(anyView as UIView)
|
|
1076
|
+
pipTargetView = anyView
|
|
1077
|
+
pipHasValidSourceView = true
|
|
1078
|
+
print("🖼️ [PiP] Set up with any available view (VALID)")
|
|
1079
|
+
} else {
|
|
1080
|
+
// Last resort: try device.previewView() - but mark as NOT valid for remote streams
|
|
1081
|
+
// This allows frame capture to start, but we'll re-setup when a remote view becomes available
|
|
1082
|
+
do {
|
|
1083
|
+
let previewView = try device.previewView()
|
|
1084
|
+
pipController.setupWithSourceView(previewView)
|
|
1085
|
+
pipTargetView = previewView
|
|
1086
|
+
// Mark as NOT valid - we need to re-setup when remote view is available
|
|
1087
|
+
pipHasValidSourceView = false
|
|
1088
|
+
print("🖼️ [PiP] Set up with device preview view (NOT VALID - will re-setup when remote view available)")
|
|
1089
|
+
} catch {
|
|
1090
|
+
print("🖼️ [PiP] ERROR: Could not get any preview view: \(error)")
|
|
1091
|
+
pipHasValidSourceView = false
|
|
1081
1092
|
}
|
|
1082
1093
|
}
|
|
1083
1094
|
}
|
|
1084
1095
|
|
|
1085
|
-
// Set frame callback to receive CVPixelBuffers
|
|
1086
|
-
// Using a dedicated queue for better performance
|
|
1096
|
+
// Set frame callback to receive CVPixelBuffers (only if not already set on this device)
|
|
1087
1097
|
let frameQueue = DispatchQueue(label: "com.ivs.pip.frameCallback", qos: .userInteractive)
|
|
1088
1098
|
device.setOnFrameCallbackQueue(frameQueue, includePixelBuffer: true) { [weak self] frame in
|
|
1089
1099
|
guard let self = self else { return }
|
|
@@ -1121,9 +1131,15 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1121
1131
|
}
|
|
1122
1132
|
|
|
1123
1133
|
if let stream = candidateStream, let imageDevice = stream.device as? IVSImageDevice {
|
|
1124
|
-
// Found a valid video stream
|
|
1125
|
-
if
|
|
1126
|
-
|
|
1134
|
+
// Found a valid video stream
|
|
1135
|
+
// Re-attach if: 1) different device, OR 2) same device but we don't have a valid source view yet
|
|
1136
|
+
let needsSetup = currentPiPSourceDeviceUrn != imageDevice.descriptor().urn || !pipHasValidSourceView
|
|
1137
|
+
|
|
1138
|
+
if needsSetup {
|
|
1139
|
+
let isNewDevice = currentPiPSourceDeviceUrn != imageDevice.descriptor().urn
|
|
1140
|
+
let reason = isNewDevice ? "new device" : "need valid source view"
|
|
1141
|
+
print("🖼️ [PiP] Setting up PiP for remote video stream (\(reason)): \(imageDevice.descriptor().urn)")
|
|
1142
|
+
print("🖼️ [PiP] pipHasValidSourceView: \(pipHasValidSourceView)")
|
|
1127
1143
|
|
|
1128
1144
|
// Debug: Log all registered remote views and their URNs
|
|
1129
1145
|
print("🖼️ [PiP] Registered remote views: \(remoteViews.count)")
|
|
@@ -1146,6 +1162,8 @@ class IVSStageManager: NSObject, IVSStageStreamDelegate, IVSStageStrategy, IVSSt
|
|
|
1146
1162
|
// Last resort: use any registered view
|
|
1147
1163
|
candidateSourceView = anyView as UIView
|
|
1148
1164
|
print("🖼️ [PiP] Using any available remote view as last resort")
|
|
1165
|
+
} else {
|
|
1166
|
+
print("🖼️ [PiP] No remote views available yet - will retry when view registers")
|
|
1149
1167
|
}
|
|
1150
1168
|
}
|
|
1151
1169
|
|