@stream-io/video-react-native-sdk 1.44.0 → 2.0.0-beta.0
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/CHANGELOG.md +26 -3
- package/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt +112 -26
- package/android/src/main/java/com/streamvideo/reactnative/util/CallAlivePermissionsHelper.kt +68 -37
- package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js +12 -6
- package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -1
- package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js +38 -11
- package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
- package/dist/commonjs/hooks/useLoopbackRecording.js +10 -127
- package/dist/commonjs/hooks/useLoopbackRecording.js.map +1 -1
- package/dist/commonjs/utils/StreamVideoRN/index.js +2 -0
- package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/commonjs/utils/internal/callingx/callingx.js +38 -56
- package/dist/commonjs/utils/internal/callingx/callingx.js.map +1 -1
- package/dist/commonjs/utils/keepCallAliveHeadlessTask.js +43 -4
- package/dist/commonjs/utils/keepCallAliveHeadlessTask.js.map +1 -1
- package/dist/commonjs/utils/push/android.js +7 -167
- package/dist/commonjs/utils/push/android.js.map +1 -1
- package/dist/commonjs/utils/push/internal/android.js +204 -0
- package/dist/commonjs/utils/push/internal/android.js.map +1 -0
- package/dist/commonjs/utils/push/libs/firebaseMessaging/index.js +9 -1
- package/dist/commonjs/utils/push/libs/firebaseMessaging/index.js.map +1 -1
- package/dist/commonjs/utils/push/libs/firebaseMessaging/lib.js +1 -1
- package/dist/commonjs/utils/push/libs/firebaseMessaging/lib.js.map +1 -1
- package/dist/commonjs/utils/push/setupAndroidPushEvents.js +25 -0
- package/dist/commonjs/utils/push/setupAndroidPushEvents.js.map +1 -0
- package/dist/commonjs/utils/push/setupCallingExpEvents.js +6 -2
- package/dist/commonjs/utils/push/setupCallingExpEvents.js.map +1 -1
- package/dist/commonjs/utils/push/setupIosVoipPushEvents.js +6 -7
- package/dist/commonjs/utils/push/setupIosVoipPushEvents.js.map +1 -1
- package/dist/commonjs/utils/push/utils.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/commonjs/version.js.map +1 -1
- package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js +12 -6
- package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -1
- package/dist/module/hooks/useAndroidKeepCallAliveEffect.js +39 -12
- package/dist/module/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
- package/dist/module/hooks/useLoopbackRecording.js +10 -127
- package/dist/module/hooks/useLoopbackRecording.js.map +1 -1
- package/dist/module/utils/StreamVideoRN/index.js +2 -0
- package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/module/utils/internal/callingx/callingx.js +38 -56
- package/dist/module/utils/internal/callingx/callingx.js.map +1 -1
- package/dist/module/utils/keepCallAliveHeadlessTask.js +41 -3
- package/dist/module/utils/keepCallAliveHeadlessTask.js.map +1 -1
- package/dist/module/utils/push/android.js +9 -169
- package/dist/module/utils/push/android.js.map +1 -1
- package/dist/module/utils/push/internal/android.js +200 -0
- package/dist/module/utils/push/internal/android.js.map +1 -0
- package/dist/module/utils/push/libs/firebaseMessaging/index.js +9 -1
- package/dist/module/utils/push/libs/firebaseMessaging/index.js.map +1 -1
- package/dist/module/utils/push/libs/firebaseMessaging/lib.js +1 -1
- package/dist/module/utils/push/libs/firebaseMessaging/lib.js.map +1 -1
- package/dist/module/utils/push/setupAndroidPushEvents.js +21 -0
- package/dist/module/utils/push/setupAndroidPushEvents.js.map +1 -0
- package/dist/module/utils/push/setupCallingExpEvents.js +6 -2
- package/dist/module/utils/push/setupCallingExpEvents.js.map +1 -1
- package/dist/module/utils/push/setupIosVoipPushEvents.js +6 -7
- package/dist/module/utils/push/setupIosVoipPushEvents.js.map +1 -1
- package/dist/module/utils/push/utils.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/module/version.js.map +1 -1
- package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts.map +1 -1
- package/dist/typescript/hooks/useAndroidKeepCallAliveEffect.d.ts.map +1 -1
- package/dist/typescript/hooks/useLoopbackRecording.d.ts +12 -69
- package/dist/typescript/hooks/useLoopbackRecording.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/types.d.ts +1 -2
- package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
- package/dist/typescript/utils/internal/callingx/callingx.d.ts.map +1 -1
- package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts +9 -0
- package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts.map +1 -1
- package/dist/typescript/utils/push/android.d.ts +3 -4
- package/dist/typescript/utils/push/android.d.ts.map +1 -1
- package/dist/typescript/utils/push/internal/android.d.ts +8 -0
- package/dist/typescript/utils/push/internal/android.d.ts.map +1 -0
- package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts +6 -4
- package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs/firebaseMessaging/lib.d.ts +2 -2
- package/dist/typescript/utils/push/libs/firebaseMessaging/lib.d.ts.map +1 -1
- package/dist/typescript/utils/push/setupAndroidPushEvents.d.ts +3 -0
- package/dist/typescript/utils/push/setupAndroidPushEvents.d.ts.map +1 -0
- package/dist/typescript/utils/push/setupCallingExpEvents.d.ts.map +1 -1
- package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts.map +1 -1
- package/dist/typescript/utils/push/utils.d.ts +2 -2
- package/dist/typescript/utils/push/utils.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/dist/typescript/version.d.ts.map +1 -1
- package/expo-config-plugin/dist/withAndroidPermissions.js +1 -1
- package/ios/TracksRecorder/RecorderVideoSink.swift +92 -5
- package/package.json +4 -4
- package/src/hooks/push/useCallingExpWithCallingStateEffect.ts +25 -11
- package/src/hooks/useAndroidKeepCallAliveEffect.ts +59 -21
- package/src/hooks/useLoopbackRecording.ts +36 -248
- package/src/utils/StreamVideoRN/index.ts +2 -0
- package/src/utils/StreamVideoRN/types.ts +1 -2
- package/src/utils/internal/callingx/callingx.ts +49 -68
- package/src/utils/keepCallAliveHeadlessTask.ts +45 -3
- package/src/utils/push/android.ts +12 -237
- package/src/utils/push/internal/android.ts +273 -0
- package/src/utils/push/libs/firebaseMessaging/index.ts +16 -5
- package/src/utils/push/libs/firebaseMessaging/lib.ts +3 -3
- package/src/utils/push/setupAndroidPushEvents.ts +25 -0
- package/src/utils/push/setupCallingExpEvents.ts +6 -3
- package/src/utils/push/setupIosVoipPushEvents.ts +8 -7
- package/src/utils/push/utils.ts +2 -4
- package/src/version.ts +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export type
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
import { type MessagingModule } from './lib';
|
|
2
|
+
export type FirebaseRemoteMessage = NonNullable<Awaited<ReturnType<MessagingModule['getInitialNotification']>>>;
|
|
3
|
+
export declare function getFirebaseMessagingLib(): () => {
|
|
4
|
+
getToken: () => Promise<string>;
|
|
5
|
+
onTokenRefresh: (listener: (token: string) => void) => () => void;
|
|
6
|
+
};
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/push/libs/firebaseMessaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/push/libs/firebaseMessaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,eAAe,EAAE,MAAM,OAAO,CAAC;AAKlD,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAC7C,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAC/D,CAAC;AAYF,wBAAgB,uBAAuB;;+BAKR,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;EAGrD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
2
|
-
declare let lib:
|
|
1
|
+
export type MessagingModule = typeof import('@react-native-firebase/messaging');
|
|
2
|
+
declare let lib: MessagingModule | undefined;
|
|
3
3
|
export { lib };
|
|
4
4
|
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/push/libs/firebaseMessaging/lib.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/push/libs/firebaseMessaging/lib.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,cAAc,kCAAkC,CAAC,CAAC;AAEhF,QAAA,IAAI,GAAG,EAAE,eAAe,GAAG,SAAS,CAAC;AAMrC,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupAndroidPushEvents.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/setupAndroidPushEvents.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,QAiBnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupCallingExpEvents.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/setupCallingExpEvents.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"setupCallingExpEvents.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/setupCallingExpEvents.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAahE,KAAK,UAAU,GAAG,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AAIzD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,QAyDxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupIosVoipPushEvents.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/setupIosVoipPushEvents.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"setupIosVoipPushEvents.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/setupIosVoipPushEvents.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,QAmBnD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FirebaseRemoteMessage } from './libs/firebaseMessaging';
|
|
2
2
|
import type { NonRingingPushEvent } from '../StreamVideoRN/types';
|
|
3
3
|
export type StreamPushPayload = {
|
|
4
4
|
call_cid: string;
|
|
5
5
|
type: 'call.ring' | NonRingingPushEvent;
|
|
6
6
|
sender: string;
|
|
7
7
|
} | undefined;
|
|
8
|
-
export declare function isFirebaseStreamVideoMessage(message:
|
|
8
|
+
export declare function isFirebaseStreamVideoMessage(message: FirebaseRemoteMessage): boolean;
|
|
9
9
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/push/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,MAAM,iBAAiB,GACzB;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,GAAG,mBAAmB,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB,GACD,SAAS,CAAC;AAEd,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,qBAAqB,WAE1E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "
|
|
1
|
+
export declare const version = "2.0.0-beta.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,iBAAiB,CAAC"}
|
|
@@ -15,7 +15,7 @@ const withStreamVideoReactNativeSDKAndroidPermissions = (configuration, props) =
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
if (props?.androidKeepCallAlive) {
|
|
18
|
-
permissions.push('android.permission.FOREGROUND_SERVICE_CAMERA', 'android.permission.FOREGROUND_SERVICE_MICROPHONE', 'android.permission.
|
|
18
|
+
permissions.push('android.permission.FOREGROUND_SERVICE_CAMERA', 'android.permission.FOREGROUND_SERVICE_MICROPHONE', 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK');
|
|
19
19
|
}
|
|
20
20
|
const config = config_plugins_1.AndroidConfig.Permissions.withPermissions(configuration, permissions);
|
|
21
21
|
return config;
|
|
@@ -12,11 +12,21 @@ import WebRTC
|
|
|
12
12
|
///
|
|
13
13
|
/// Each delivered `RTCVideoFrame` is normalised to a CVPixelBuffer in the
|
|
14
14
|
/// hardware H.264 encoder's native format — **NV12**
|
|
15
|
-
/// (`kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange`).
|
|
16
|
-
/// `
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
15
|
+
/// (`kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange`). One branch per
|
|
16
|
+
/// buffer type `renderFrame` can be handed:
|
|
17
|
+
///
|
|
18
|
+
/// - `RTCCVPixelBuffer` (camera passthrough) already wraps a CVPixelBuffer,
|
|
19
|
+
/// so the underlying buffer is forwarded unchanged.
|
|
20
|
+
/// - `RTCNV12Buffer` already holds NV12 planes, but as raw pointers rather
|
|
21
|
+
/// than a CVPixelBuffer, so the planes are copied row-wise into a fresh
|
|
22
|
+
/// one. No format conversion. This is the buffer type decoded remote
|
|
23
|
+
/// frames arrive as, so it is the hot path for loopback recording — do
|
|
24
|
+
/// not remove it. Without it these frames fall through to `toI420()`
|
|
25
|
+
/// below, which converts NV12 → I420 → NV12 and caps throughput at
|
|
26
|
+
/// roughly half the source frame rate.
|
|
27
|
+
/// - `RTCI420Buffer` (and anything else, via `toI420()`) is converted into
|
|
28
|
+
/// a fresh IOSurface-backed NV12 buffer by interleaving the U and V
|
|
29
|
+
/// planes — a plane reorder, no colour-space conversion.
|
|
20
30
|
///
|
|
21
31
|
/// **Why NV12 and not BGRA?** AVAssetWriter's hardware encoder accepts
|
|
22
32
|
/// both, but BGRA requires an internal colour-space conversion that
|
|
@@ -51,6 +61,8 @@ import WebRTC
|
|
|
51
61
|
if let cvBuffer = frame.buffer as? RTCCVPixelBuffer {
|
|
52
62
|
// Camera passthrough — already a CVPixelBuffer (typically NV12 on iOS).
|
|
53
63
|
pixelBuffer = cvBuffer.pixelBuffer
|
|
64
|
+
} else if let nv12 = frame.buffer as? RTCNV12Buffer {
|
|
65
|
+
pixelBuffer = Self.makeNV12PixelBuffer(fromNV12: nv12)
|
|
54
66
|
} else if let i420 = frame.buffer as? RTCI420Buffer {
|
|
55
67
|
pixelBuffer = Self.makeNV12PixelBuffer(fromI420: i420)
|
|
56
68
|
} else {
|
|
@@ -67,6 +79,81 @@ import WebRTC
|
|
|
67
79
|
frameHandler(outputBuffer, frame.width, frame.height, frame.timeStampNs)
|
|
68
80
|
}
|
|
69
81
|
|
|
82
|
+
private static func makeNV12PixelBuffer(fromNV12 nv12: RTCNV12Buffer) -> CVPixelBuffer? {
|
|
83
|
+
let width = Int(nv12.width)
|
|
84
|
+
let height = Int(nv12.height)
|
|
85
|
+
let chromaWidth = Int(nv12.chromaWidth)
|
|
86
|
+
let chromaHeight = Int(nv12.chromaHeight)
|
|
87
|
+
|
|
88
|
+
guard let buffer = makePixelBuffer(width: width, height: height) else { return nil }
|
|
89
|
+
|
|
90
|
+
CVPixelBufferLockBaseAddress(buffer, [])
|
|
91
|
+
defer { CVPixelBufferUnlockBaseAddress(buffer, []) }
|
|
92
|
+
|
|
93
|
+
guard let yDest = CVPixelBufferGetBaseAddressOfPlane(buffer, 0) else { return nil }
|
|
94
|
+
copyPlane(
|
|
95
|
+
source: UnsafeRawPointer(nv12.dataY),
|
|
96
|
+
sourceStride: Int(nv12.strideY),
|
|
97
|
+
destination: yDest,
|
|
98
|
+
destinationStride: CVPixelBufferGetBytesPerRowOfPlane(buffer, 0),
|
|
99
|
+
rowBytes: width,
|
|
100
|
+
rows: height
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
// Plane 1: UV — already interleaved, so two bytes per chroma sample.
|
|
104
|
+
guard let uvDest = CVPixelBufferGetBaseAddressOfPlane(buffer, 1) else { return nil }
|
|
105
|
+
copyPlane(
|
|
106
|
+
source: UnsafeRawPointer(nv12.dataUV),
|
|
107
|
+
sourceStride: Int(nv12.strideUV),
|
|
108
|
+
destination: uvDest,
|
|
109
|
+
destinationStride: CVPixelBufferGetBytesPerRowOfPlane(buffer, 1),
|
|
110
|
+
rowBytes: chromaWidth * 2,
|
|
111
|
+
rows: chromaHeight
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return buffer
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private static func makePixelBuffer(width: Int, height: Int) -> CVPixelBuffer? {
|
|
118
|
+
var pixelBuffer: CVPixelBuffer?
|
|
119
|
+
let attrs: [String: Any] = [
|
|
120
|
+
kCVPixelBufferIOSurfacePropertiesKey as String: [:] as [String: Any],
|
|
121
|
+
]
|
|
122
|
+
let status = CVPixelBufferCreate(
|
|
123
|
+
kCFAllocatorDefault,
|
|
124
|
+
width,
|
|
125
|
+
height,
|
|
126
|
+
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
|
127
|
+
attrs as CFDictionary,
|
|
128
|
+
&pixelBuffer
|
|
129
|
+
)
|
|
130
|
+
guard status == kCVReturnSuccess else { return nil }
|
|
131
|
+
return pixelBuffer
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Row-wise copy, collapsing to a single `memcpy` when the strides match.
|
|
135
|
+
private static func copyPlane(
|
|
136
|
+
source: UnsafeRawPointer,
|
|
137
|
+
sourceStride: Int,
|
|
138
|
+
destination: UnsafeMutableRawPointer,
|
|
139
|
+
destinationStride: Int,
|
|
140
|
+
rowBytes: Int,
|
|
141
|
+
rows: Int
|
|
142
|
+
) {
|
|
143
|
+
if sourceStride == destinationStride && sourceStride == rowBytes {
|
|
144
|
+
memcpy(destination, source, rowBytes * rows)
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
let copyBytes = min(rowBytes, min(sourceStride, destinationStride))
|
|
148
|
+
for row in 0..<rows {
|
|
149
|
+
memcpy(
|
|
150
|
+
destination.advanced(by: row * destinationStride),
|
|
151
|
+
source.advanced(by: row * sourceStride),
|
|
152
|
+
copyBytes
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
70
157
|
// MARK: - I420 → NV12
|
|
71
158
|
|
|
72
159
|
/// Allocates a fresh IOSurface-backed NV12 `CVPixelBuffer` and copies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-native-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
4
|
"description": "Stream Video SDK for React Native",
|
|
5
5
|
"author": "https://getstream.io",
|
|
6
6
|
"homepage": "https://getstream.io/video/docs/react-native/",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"!**/.*"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@stream-io/video-client": "
|
|
56
|
-
"@stream-io/video-react-bindings": "
|
|
55
|
+
"@stream-io/video-client": "2.0.0-beta.0",
|
|
56
|
+
"@stream-io/video-react-bindings": "2.0.0-beta.0",
|
|
57
57
|
"intl-pluralrules": "2.0.1",
|
|
58
58
|
"react-native-url-polyfill": "^3.0.0",
|
|
59
59
|
"rxjs": "~7.8.2",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@react-native/jest-preset": "0.86.2",
|
|
119
119
|
"@react-native/metro-config": "0.86.2",
|
|
120
120
|
"@stream-io/noise-cancellation-react-native": "^0.11.0",
|
|
121
|
-
"@stream-io/react-native-callingx": "^0.
|
|
121
|
+
"@stream-io/react-native-callingx": "^0.11.1",
|
|
122
122
|
"@stream-io/react-native-webrtc": "145.3.1",
|
|
123
123
|
"@stream-io/typescript-config": "^0.0.0",
|
|
124
124
|
"@stream-io/video-filters-react-native": "^0.16.0",
|
|
@@ -17,7 +17,7 @@ export const useCallingExpWithCallingStateEffect = () => {
|
|
|
17
17
|
useCallStateHooks();
|
|
18
18
|
|
|
19
19
|
const activeCall = useCall();
|
|
20
|
-
const {
|
|
20
|
+
const { optimisticStatus, microphone } = useMicrophoneState();
|
|
21
21
|
const callMembers = useCallMembers();
|
|
22
22
|
const participants = useParticipants();
|
|
23
23
|
|
|
@@ -30,7 +30,7 @@ export const useCallingExpWithCallingStateEffect = () => {
|
|
|
30
30
|
|
|
31
31
|
const callDisplayName = useMemo(
|
|
32
32
|
() =>
|
|
33
|
-
callCustomDisplayName
|
|
33
|
+
callCustomDisplayName ||
|
|
34
34
|
getCallDisplayName(callMembers, participants, currentUserId),
|
|
35
35
|
[callMembers, participants, currentUserId, callCustomDisplayName],
|
|
36
36
|
);
|
|
@@ -112,12 +112,19 @@ export const useCallingExpWithCallingStateEffect = () => {
|
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
callingx
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
callingx
|
|
116
|
+
.updateDisplay(
|
|
117
|
+
activeCallCid,
|
|
118
|
+
createdByUserId ?? callDisplayName,
|
|
119
|
+
callDisplayName,
|
|
120
|
+
isIncoming,
|
|
121
|
+
)
|
|
122
|
+
.catch((error: unknown) => {
|
|
123
|
+
logger.debug(
|
|
124
|
+
`useCallingExpWithCallingStateEffect: Error updating display in callingx: ${activeCallCid}`,
|
|
125
|
+
error,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
121
128
|
}, [activeCallCid, createdByUserId, callDisplayName, isIncoming]);
|
|
122
129
|
|
|
123
130
|
// Sync microphone mute state from app → CallKit
|
|
@@ -135,8 +142,14 @@ export const useCallingExpWithCallingStateEffect = () => {
|
|
|
135
142
|
return;
|
|
136
143
|
}
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
if (optimisticStatus === undefined) {
|
|
146
|
+
logger.debug(
|
|
147
|
+
`useCallingExpWithCallingStateEffect: mic status is not resolved yet, skipping mute sync for: ${activeCallCid}`,
|
|
148
|
+
);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
callingx.setMutedCall(activeCallCid, optimisticStatus === 'disabled');
|
|
152
|
+
}, [activeCallCid, optimisticStatus]);
|
|
140
153
|
|
|
141
154
|
// Sync mute state from CallKit → app (only for system-initiated mute actions)
|
|
142
155
|
useEffect(() => {
|
|
@@ -165,7 +178,8 @@ export const useCallingExpWithCallingStateEffect = () => {
|
|
|
165
178
|
return;
|
|
166
179
|
}
|
|
167
180
|
|
|
168
|
-
const isCurrentlyMuted =
|
|
181
|
+
const isCurrentlyMuted =
|
|
182
|
+
microphone.state.optimisticStatus === 'disabled';
|
|
169
183
|
if (isCurrentlyMuted === muted) {
|
|
170
184
|
logger.debug(
|
|
171
185
|
`useCallingExpWithCallingStateEffect: Mic toggle is already in the desired state: ${muted} for call: ${activeCallCid}`,
|
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
Platform,
|
|
10
10
|
} from 'react-native';
|
|
11
11
|
import { CallingState, videoLoggerSystem } from '@stream-io/video-client';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
endKeepCallAliveHeadlessTask,
|
|
14
|
+
keepCallAliveCallRef,
|
|
15
|
+
} from '../utils/keepCallAliveHeadlessTask';
|
|
13
16
|
import { getCallingxLibIfAvailable } from '../utils/push/libs';
|
|
14
17
|
|
|
15
18
|
async function stopForegroundServiceNoThrow() {
|
|
@@ -21,7 +24,10 @@ async function stopForegroundServiceNoThrow() {
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
/**
|
|
28
|
+
* @returns true when the native foreground service was asked to start.
|
|
29
|
+
*/
|
|
30
|
+
async function startForegroundService(call_cid: string): Promise<boolean> {
|
|
25
31
|
const logger = videoLoggerSystem.getLogger('startForegroundService');
|
|
26
32
|
const isCallAliveConfigured = await (async () => {
|
|
27
33
|
try {
|
|
@@ -32,10 +38,14 @@ async function startForegroundService(call_cid: string) {
|
|
|
32
38
|
}
|
|
33
39
|
})();
|
|
34
40
|
if (!isCallAliveConfigured) {
|
|
35
|
-
logger.
|
|
36
|
-
'KeepCallAlive is not configured
|
|
41
|
+
logger.warn(
|
|
42
|
+
'KeepCallAlive is not configured, the call will not survive the app going to the ' +
|
|
43
|
+
'background: audio publishing stops and the user is eventually dropped from the call. ' +
|
|
44
|
+
'Declare android.permission.FOREGROUND_SERVICE and ' +
|
|
45
|
+
'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK in the app manifest (see logcat, ' +
|
|
46
|
+
'tag StreamVideoReactNative, for the exact list of missing permissions).',
|
|
37
47
|
);
|
|
38
|
-
return;
|
|
48
|
+
return false;
|
|
39
49
|
}
|
|
40
50
|
// Check for notification permission (Android 13+) before starting the service.
|
|
41
51
|
const hasPostNotificationsPermission =
|
|
@@ -47,7 +57,7 @@ async function startForegroundService(call_cid: string) {
|
|
|
47
57
|
logger.info(
|
|
48
58
|
'Notification permission not granted, can not start foreground service to keep the call alive',
|
|
49
59
|
);
|
|
50
|
-
return;
|
|
60
|
+
return false;
|
|
51
61
|
}
|
|
52
62
|
const videoConfig = StreamVideoRN.getConfig();
|
|
53
63
|
const foregroundServiceConfig = videoConfig.foregroundService;
|
|
@@ -58,19 +68,34 @@ async function startForegroundService(call_cid: string) {
|
|
|
58
68
|
// NOTE: we use requestAnimationFrame to ensure that the foreground service is started after all the current UI operations are done
|
|
59
69
|
// this is a workaround for the crash - android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()
|
|
60
70
|
// this crash was reproducible only in some android devices
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
return new Promise<boolean>((resolve) => {
|
|
72
|
+
requestAnimationFrame(async () => {
|
|
73
|
+
// The service declares the camera/microphone foreground service types, which are
|
|
74
|
+
// while-in-use permissions: Android requires startForegroundService() to be called while the
|
|
75
|
+
// app has a visible activity, otherwise the start is rejected. The frame we deferred to can
|
|
76
|
+
// land after the app left the foreground, so re-check before calling native.
|
|
77
|
+
if (AppState.currentState !== 'active') {
|
|
78
|
+
logger.info(
|
|
79
|
+
'App is no longer in the foreground, not starting the keep-call-alive foreground service',
|
|
80
|
+
);
|
|
81
|
+
resolve(false);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await NativeModules.StreamVideoReactNative.startKeepCallAliveService(
|
|
86
|
+
call_cid,
|
|
87
|
+
channel.id,
|
|
88
|
+
channel.name,
|
|
89
|
+
notificationTexts.title,
|
|
90
|
+
notificationTexts.body,
|
|
91
|
+
smallIconName ?? null,
|
|
92
|
+
);
|
|
93
|
+
resolve(true);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
logger.warn('Failed to start keep-call-alive foreground service', e);
|
|
96
|
+
resolve(false);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
74
99
|
});
|
|
75
100
|
}
|
|
76
101
|
|
|
@@ -147,8 +172,18 @@ export const useAndroidKeepCallAliveEffect = () => {
|
|
|
147
172
|
return;
|
|
148
173
|
}
|
|
149
174
|
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
try {
|
|
176
|
+
// only mark as started when native actually accepted the start, so that a later render
|
|
177
|
+
// while still joined can retry.
|
|
178
|
+
foregroundServiceStartedRef.current =
|
|
179
|
+
await startForegroundService(activeCallCid);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
// run() is intentionally not awaited, so anything thrown here would surface as an
|
|
182
|
+
// unhandled rejection. Leave the ref false so a later render can retry.
|
|
183
|
+
videoLoggerSystem
|
|
184
|
+
.getLogger('useAndroidKeepCallAliveEffect')
|
|
185
|
+
.warn('Failed to start the keep-call-alive foreground service', e);
|
|
186
|
+
}
|
|
152
187
|
};
|
|
153
188
|
|
|
154
189
|
// ensure that app is active before running the function
|
|
@@ -170,6 +205,8 @@ export const useAndroidKeepCallAliveEffect = () => {
|
|
|
170
205
|
} else if (isCallEnded) {
|
|
171
206
|
if (foregroundServiceStartedRef.current) {
|
|
172
207
|
keepCallAliveCallRef.current = undefined;
|
|
208
|
+
// let the headless task resolve so RN unwinds it, then stop the foreground service
|
|
209
|
+
endKeepCallAliveHeadlessTask();
|
|
173
210
|
// stop foreground service when the call is not active
|
|
174
211
|
stopForegroundServiceNoThrow();
|
|
175
212
|
foregroundServiceStartedRef.current = false;
|
|
@@ -188,6 +225,7 @@ export const useAndroidKeepCallAliveEffect = () => {
|
|
|
188
225
|
// stop foreground service when this effect is unmounted
|
|
189
226
|
if (foregroundServiceStartedRef.current) {
|
|
190
227
|
keepCallAliveCallRef.current = undefined;
|
|
228
|
+
endKeepCallAliveHeadlessTask();
|
|
191
229
|
stopForegroundServiceNoThrow();
|
|
192
230
|
foregroundServiceStartedRef.current = false;
|
|
193
231
|
}
|