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.
@@ -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
- // Try to get a preview view from the device
1056
- do {
1057
- let previewView = try device.previewView()
1058
- pipController.setupWithSourceView(previewView)
1059
- pipTargetView = previewView
1060
- print("🖼️ [PiP] Set up with device preview view")
1061
- } catch {
1062
- print("🖼️ [PiP] Warning: Could not get preview view from device: \(error)")
1063
- // Try to find a remote view that's rendering this device
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
1066
- pipController.setupWithSourceView(remoteView as UIView)
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!")
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, attach to it
1125
- if currentPiPSourceDeviceUrn != imageDevice.descriptor().urn {
1126
- print("🖼️ [PiP] Found new remote video stream: \(imageDevice.descriptor().urn)")
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-realtime-ivs-broadcast",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "An Expo module for real-time broadcasting using Amazon IVS.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",