@volcengine/react-native-live-push 1.1.3-rc.1 → 1.3.0-rc.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.
Files changed (139) hide show
  1. package/android/build.gradle +2 -2
  2. package/android/src/main/java/com/volcengine/velive/rn/push/ClassHelper.java +9 -0
  3. package/android/src/main/java/com/volcengine/velive/rn/push/NativeVariableManager.java +5 -8
  4. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushModule.java +14 -2
  5. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushPackage.java +16 -13
  6. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushView.java +16 -0
  7. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushViewManager.java +7 -2
  8. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +410 -0
  9. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +328 -0
  10. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerViewManager.java +43 -0
  11. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/TextureMgr.java +168 -0
  12. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/YuvHelper.java +165 -0
  13. package/ios/VeLiveMixerHelper.h +49 -0
  14. package/ios/VeLiveMixerHelper.m +646 -0
  15. package/ios/VeLiveMixerView.h +62 -0
  16. package/ios/VeLiveMixerView.m +547 -0
  17. package/ios/VeLiveMixerViewManager.m +56 -0
  18. package/lib/commonjs/index.js +22697 -20359
  19. package/lib/commonjs/typescript/android/index.d.ts +44 -0
  20. package/lib/commonjs/typescript/codegen/android/api.d.ts +1068 -0
  21. package/lib/commonjs/typescript/codegen/android/callback.d.ts +333 -0
  22. package/lib/commonjs/typescript/codegen/android/errorcode.d.ts +92 -0
  23. package/lib/commonjs/typescript/codegen/android/index.d.ts +5 -0
  24. package/lib/commonjs/typescript/codegen/android/keytype.d.ts +1693 -0
  25. package/lib/commonjs/typescript/codegen/android/types.d.ts +33 -0
  26. package/lib/commonjs/typescript/codegen/ios/api.d.ts +1125 -0
  27. package/lib/commonjs/typescript/codegen/ios/callback.d.ts +242 -0
  28. package/lib/commonjs/typescript/codegen/ios/errorcode.d.ts +154 -0
  29. package/lib/commonjs/typescript/codegen/ios/external.d.ts +1 -0
  30. package/lib/commonjs/typescript/codegen/ios/index.d.ts +6 -0
  31. package/lib/commonjs/typescript/codegen/ios/keytype.d.ts +1154 -0
  32. package/lib/commonjs/typescript/codegen/ios/types.d.ts +46 -0
  33. package/lib/commonjs/typescript/codegen/pack/api.d.ts +1470 -0
  34. package/lib/commonjs/typescript/codegen/pack/callback.d.ts +446 -0
  35. package/lib/commonjs/typescript/codegen/pack/errorcode.d.ts +109 -0
  36. package/lib/commonjs/typescript/codegen/pack/index.d.ts +5 -0
  37. package/lib/commonjs/typescript/codegen/pack/keytype.d.ts +2248 -0
  38. package/lib/commonjs/typescript/codegen/pack/types.d.ts +68 -0
  39. package/lib/commonjs/typescript/codegen/type-shim.d.ts +6 -0
  40. package/lib/commonjs/typescript/component.d.ts +15 -0
  41. package/lib/commonjs/typescript/core/api.d.ts +18 -0
  42. package/lib/commonjs/typescript/core/callback.d.ts +2 -0
  43. package/lib/commonjs/typescript/core/env.d.ts +29 -0
  44. package/lib/commonjs/typescript/core/errorcode.d.ts +2 -0
  45. package/lib/commonjs/typescript/core/index.d.ts +6 -0
  46. package/lib/commonjs/typescript/core/keytype.d.ts +17 -0
  47. package/lib/commonjs/typescript/core/mixer.d.ts +26 -0
  48. package/lib/commonjs/typescript/core/pusher.d.ts +13 -0
  49. package/lib/commonjs/typescript/index.d.ts +3 -0
  50. package/lib/commonjs/typescript/ios/extends.d.ts +41 -0
  51. package/lib/commonjs/typescript/platforms/android/extends.d.ts +8 -0
  52. package/lib/commonjs/typescript/platforms/android/helper.d.ts +8 -0
  53. package/lib/commonjs/typescript/platforms/android/mixer.d.ts +8 -0
  54. package/lib/commonjs/typescript/platforms/ios/extends.d.ts +17 -0
  55. package/lib/commonjs/typescript/platforms/ios/helper.d.ts +8 -0
  56. package/lib/commonjs/typescript/platforms/ios/mixer.d.ts +9 -0
  57. package/lib/commonjs/typescript/runtime.d.ts +1 -0
  58. package/lib/commonjs/typescript/view/MixView.d.ts +44 -0
  59. package/lib/commonjs/typescript/view/VeImageView.d.ts +19 -0
  60. package/lib/commonjs/typescript/view/VeTextView.d.ts +7 -0
  61. package/lib/commonjs/typescript/view/VeView.d.ts +7 -0
  62. package/lib/commonjs/typescript/view/VeWebView.d.ts +7 -0
  63. package/lib/commonjs/typescript/view/index.d.ts +5 -0
  64. package/lib/module/index.js +22694 -20360
  65. package/lib/module/typescript/android/index.d.ts +44 -0
  66. package/lib/module/typescript/codegen/android/api.d.ts +1068 -0
  67. package/lib/module/typescript/codegen/android/callback.d.ts +333 -0
  68. package/lib/module/typescript/codegen/android/errorcode.d.ts +92 -0
  69. package/lib/module/typescript/codegen/android/index.d.ts +5 -0
  70. package/lib/module/typescript/codegen/android/keytype.d.ts +1693 -0
  71. package/lib/module/typescript/codegen/android/types.d.ts +33 -0
  72. package/lib/module/typescript/codegen/ios/api.d.ts +1125 -0
  73. package/lib/module/typescript/codegen/ios/callback.d.ts +242 -0
  74. package/lib/module/typescript/codegen/ios/errorcode.d.ts +154 -0
  75. package/lib/module/typescript/codegen/ios/external.d.ts +1 -0
  76. package/lib/module/typescript/codegen/ios/index.d.ts +6 -0
  77. package/lib/module/typescript/codegen/ios/keytype.d.ts +1154 -0
  78. package/lib/module/typescript/codegen/ios/types.d.ts +46 -0
  79. package/lib/module/typescript/codegen/pack/api.d.ts +1470 -0
  80. package/lib/module/typescript/codegen/pack/callback.d.ts +446 -0
  81. package/lib/module/typescript/codegen/pack/errorcode.d.ts +109 -0
  82. package/lib/module/typescript/codegen/pack/index.d.ts +5 -0
  83. package/lib/module/typescript/codegen/pack/keytype.d.ts +2248 -0
  84. package/lib/module/typescript/codegen/pack/types.d.ts +68 -0
  85. package/lib/module/typescript/codegen/type-shim.d.ts +6 -0
  86. package/lib/module/typescript/component.d.ts +15 -0
  87. package/lib/module/typescript/core/api.d.ts +18 -0
  88. package/lib/module/typescript/core/callback.d.ts +2 -0
  89. package/lib/module/typescript/core/env.d.ts +29 -0
  90. package/lib/module/typescript/core/errorcode.d.ts +2 -0
  91. package/lib/module/typescript/core/index.d.ts +6 -0
  92. package/lib/module/typescript/core/keytype.d.ts +17 -0
  93. package/lib/module/typescript/core/mixer.d.ts +26 -0
  94. package/lib/module/typescript/core/pusher.d.ts +13 -0
  95. package/lib/module/typescript/index.d.ts +3 -0
  96. package/lib/module/typescript/ios/extends.d.ts +41 -0
  97. package/lib/module/typescript/platforms/android/extends.d.ts +8 -0
  98. package/lib/module/typescript/platforms/android/helper.d.ts +8 -0
  99. package/lib/module/typescript/platforms/android/mixer.d.ts +8 -0
  100. package/lib/module/typescript/platforms/ios/extends.d.ts +17 -0
  101. package/lib/module/typescript/platforms/ios/helper.d.ts +8 -0
  102. package/lib/module/typescript/platforms/ios/mixer.d.ts +9 -0
  103. package/lib/module/typescript/runtime.d.ts +1 -0
  104. package/lib/module/typescript/view/MixView.d.ts +44 -0
  105. package/lib/module/typescript/view/VeImageView.d.ts +19 -0
  106. package/lib/module/typescript/view/VeTextView.d.ts +7 -0
  107. package/lib/module/typescript/view/VeView.d.ts +7 -0
  108. package/lib/module/typescript/view/VeWebView.d.ts +7 -0
  109. package/lib/module/typescript/view/index.d.ts +5 -0
  110. package/lib/typescript/android/index.d.ts +0 -3
  111. package/lib/typescript/codegen/android/api.d.ts +194 -762
  112. package/lib/typescript/codegen/android/callback.d.ts +85 -48
  113. package/lib/typescript/codegen/android/errorcode.d.ts +30 -0
  114. package/lib/typescript/codegen/android/keytype.d.ts +514 -122
  115. package/lib/typescript/codegen/ios/api.d.ts +380 -351
  116. package/lib/typescript/codegen/ios/callback.d.ts +33 -6
  117. package/lib/typescript/codegen/ios/errorcode.d.ts +52 -2
  118. package/lib/typescript/codegen/ios/keytype.d.ts +313 -35
  119. package/lib/typescript/codegen/pack/api.d.ts +302 -821
  120. package/lib/typescript/codegen/pack/callback.d.ts +54 -49
  121. package/lib/typescript/codegen/pack/errorcode.d.ts +38 -5
  122. package/lib/typescript/codegen/pack/keytype.d.ts +672 -228
  123. package/lib/typescript/core/api.d.ts +18 -2
  124. package/lib/typescript/core/keytype.d.ts +15 -0
  125. package/lib/typescript/core/mixer.d.ts +26 -0
  126. package/lib/typescript/core/pusher.d.ts +0 -3
  127. package/lib/typescript/index.d.ts +1 -0
  128. package/lib/typescript/platforms/android/extends.d.ts +8 -0
  129. package/lib/typescript/platforms/android/mixer.d.ts +8 -0
  130. package/lib/typescript/platforms/ios/mixer.d.ts +9 -0
  131. package/lib/typescript/view/MixView.d.ts +44 -0
  132. package/lib/typescript/view/VeImageView.d.ts +19 -0
  133. package/lib/typescript/view/VeTextView.d.ts +7 -0
  134. package/lib/typescript/view/VeView.d.ts +7 -0
  135. package/lib/typescript/view/VeWebView.d.ts +7 -0
  136. package/lib/typescript/view/index.d.ts +5 -0
  137. package/package.json +1 -1
  138. package/react-native-velive-push.podspec +3 -3
  139. package/android/src/main/java/com/volcengine/velive/rn/push/ScreenCaptureHelper.java +0 -73
@@ -0,0 +1,62 @@
1
+ //
2
+ // VeLiveMixerView.h
3
+ // react-native-live-push
4
+ //
5
+ // Created by ByteDance on 2025/06/09.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+ #import <UIKit/UIKit.h>
10
+ #import <React/RCTComponent.h>
11
+ #import <CoreVideo/CoreVideo.h>
12
+ #import <CoreMedia/CoreMedia.h>
13
+
14
+ NS_ASSUME_NONNULL_BEGIN
15
+
16
+ // Bitmap捕获回调协议 - 对应Android的BitmapCaptureCallback
17
+ @protocol VeLiveBitmapCaptureCallback <NSObject>
18
+ - (void)onBitmapCaptured:(UIImage *)bitmap;
19
+ - (void)onCaptureError:(NSString *)error;
20
+ @end
21
+
22
+ // MixerView代理协议
23
+ @protocol VeLiveMixerViewDelegate <NSObject>
24
+ - (void)onBitmapCaptured:(UIImage *)image viewId:(NSString *)viewId;
25
+ - (void)onCaptureError:(NSString *)error viewId:(NSString *)viewId;
26
+ @end
27
+
28
+ @interface VeLiveMixerUIView : UIView
29
+
30
+ // 视图标识
31
+ @property (nonatomic, strong) NSString *viewId;
32
+
33
+ // 捕获配置属性
34
+ @property (nonatomic, strong) NSString *captureMode;
35
+ @property (nonatomic, strong) NSNumber *captureFramerate;
36
+
37
+ // React Native 事件回调
38
+ @property (nonatomic, copy) RCTBubblingEventBlock onBitmapCapture;
39
+ @property (nonatomic, copy) RCTBubblingEventBlock onCaptureError;
40
+
41
+ // 代理
42
+ @property (nonatomic, weak) id<VeLiveMixerViewDelegate> delegate;
43
+
44
+ // Bitmap捕获回调 - 用于与MixerManager集成
45
+ @property (nonatomic, weak) id<VeLiveBitmapCaptureCallback> bitmapCaptureCallback;
46
+
47
+ // 控制方法
48
+ - (void)startCapture;
49
+ - (void)stopCapture;
50
+ - (void)performCaptureWithTrigger:(NSString *)trigger;
51
+
52
+ // 设置bitmap捕获回调
53
+ - (void)setBitmapCaptureCallback:(id<VeLiveBitmapCaptureCallback>)callback;
54
+
55
+ // UIView转换方法
56
+ - (UIImage *)snapshotIncludeSubviews;
57
+ - (CVPixelBufferRef)createPixelBuffer;
58
+ - (CMSampleBufferRef)createSampleBuffer;
59
+
60
+ @end
61
+
62
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,547 @@
1
+ //
2
+ // VeLiveMixerView.m
3
+ // react-native-live-push
4
+ //
5
+ // Created by ByteDance on 2024/12/09.
6
+ //
7
+
8
+ #import "VeLiveMixerView.h"
9
+ #import "VeLiveMixerHelper.h"
10
+ #import <AVFoundation/AVFoundation.h>
11
+ #import <CoreImage/CoreImage.h>
12
+ #import <CoreMedia/CoreMedia.h>
13
+ #import <CoreVideo/CoreVideo.h>
14
+ #import <UIKit/UIKit.h>
15
+
16
+ @interface VeLiveMixerUIView ()
17
+
18
+ // Capture configuration - Internal properties
19
+ @property(nonatomic, strong) NSString *internalCaptureMode;
20
+ @property(nonatomic, assign) float internalCaptureFramerate;
21
+
22
+ // Capture state - Align with Android
23
+ @property(nonatomic, strong) NSTimer *captureTimer;
24
+ @property(nonatomic, assign) BOOL isCapturing;
25
+ @property(nonatomic, assign) BOOL isDestroyed;
26
+ @property(nonatomic, assign) CFTimeInterval lastCaptureTime;
27
+
28
+ // Performance optimization - Align with Android
29
+ @property(nonatomic) CVPixelBufferRef lastPixelBuffer;
30
+
31
+ @end
32
+
33
+ @implementation VeLiveMixerUIView
34
+
35
+ - (instancetype)initWithFrame:(CGRect)frame {
36
+ self = [super initWithFrame:frame];
37
+ if (self) {
38
+ [self initializeCapture];
39
+ [self setupUIView];
40
+ }
41
+ return self;
42
+ }
43
+
44
+ - (void)setupUIView {
45
+ // Reference VeLivePushView.m settings, but keep our mixed view features
46
+ self.clipsToBounds = YES; // Changed to YES, consistent with VeLivePushView
47
+ self.userInteractionEnabled = YES;
48
+
49
+ // Set default background color to transparent, allowing subviews to display
50
+ self.backgroundColor = [UIColor clearColor];
51
+ }
52
+
53
+ - (void)initializeCapture {
54
+ // Initialize defaults - Completely align with Android
55
+ _internalCaptureMode = @"onchange";
56
+ _internalCaptureFramerate = 5.0f; // realTime mode default 5 fps
57
+
58
+ _isCapturing = NO;
59
+ _isDestroyed = NO;
60
+ _lastCaptureTime = 0;
61
+ _lastPixelBuffer = NULL;
62
+ }
63
+
64
+ #pragma mark - Property Getters/Setters - Align with React Native props
65
+
66
+ - (NSString *)captureMode {
67
+ return self.internalCaptureMode;
68
+ }
69
+
70
+ - (void)setCaptureMode:(NSString *)mode {
71
+ if ([mode isEqualToString:self.internalCaptureMode])
72
+ return;
73
+
74
+ [self stopCapture];
75
+ _internalCaptureMode = mode;
76
+ [self startCapture];
77
+ }
78
+
79
+ - (NSNumber *)captureFramerate {
80
+ return @(self.internalCaptureFramerate);
81
+ }
82
+
83
+ - (void)setCaptureFramerate:(NSNumber *)framerate {
84
+ _internalCaptureFramerate = MAX(1.0f, MIN(60.0f, [framerate floatValue]));
85
+ if ([self.internalCaptureMode isEqualToString:@"realtime"]) {
86
+ [self restartRealtimeCapture];
87
+ }
88
+ }
89
+
90
+ - (void)setViewId:(NSString *)viewId {
91
+ if (!viewId) {
92
+ return;
93
+ }
94
+
95
+ // Set viewId
96
+ _viewId = [viewId copy];
97
+
98
+ // Register to global cache - Align with Android's onAttachedToWindow
99
+ NSMutableDictionary *cachedViews = [VeLiveMixerHelper cachedMixedViews];
100
+ if (cachedViews) {
101
+ [cachedViews setObject:self forKey:viewId];
102
+
103
+ // Notify MixerHelper view is ready, check for pending callbacks
104
+ [VeLiveMixerHelper onViewReady:viewId mixerView:self];
105
+ }
106
+ }
107
+
108
+ #pragma mark - Capture Methods - Completely align with Android
109
+
110
+ - (void)startCapture {
111
+ if (self.isDestroyed)
112
+ return;
113
+
114
+ if ([self.internalCaptureMode isEqualToString:@"onchange"]) {
115
+ [self startOnChangeCapture];
116
+ } else if ([self.internalCaptureMode isEqualToString:@"realtime"]) {
117
+ [self startRealtimeCapture];
118
+ }
119
+ // manual mode doesn't auto-start
120
+ }
121
+
122
+ - (void)stopCapture {
123
+ [self.captureTimer invalidate];
124
+ self.captureTimer = nil;
125
+ self.isCapturing = NO;
126
+ }
127
+
128
+ - (void)startOnChangeCapture {
129
+ // onchange mode triggers every 2 seconds
130
+ self.captureTimer =
131
+ [NSTimer scheduledTimerWithTimeInterval:2.0
132
+ target:self
133
+ selector:@selector(onChangeCapture)
134
+ userInfo:nil
135
+ repeats:YES];
136
+ }
137
+
138
+ - (void)onChangeCapture {
139
+ if (!self.isDestroyed &&
140
+ [self.internalCaptureMode isEqualToString:@"onchange"]) {
141
+ @autoreleasepool {
142
+ [self performCapture:@"onchange"];
143
+ }
144
+ }
145
+ }
146
+
147
+ - (void)startRealtimeCapture {
148
+ long interval = (long)(1000.0f / self.internalCaptureFramerate);
149
+ self.captureTimer =
150
+ [NSTimer scheduledTimerWithTimeInterval:interval / 1000.0
151
+ target:self
152
+ selector:@selector(realtimeCapture)
153
+ userInfo:nil
154
+ repeats:YES];
155
+ }
156
+
157
+ - (void)realtimeCapture {
158
+ if (!self.isDestroyed &&
159
+ [self.internalCaptureMode isEqualToString:@"realtime"]) {
160
+ @autoreleasepool {
161
+ [self performCapture:@"realtime"];
162
+
163
+ // Every 5 realtime captures, perform background memory cleanup
164
+ static int captureCount = 0;
165
+ captureCount++;
166
+ if (captureCount % 5 == 0) {
167
+ dispatch_async(
168
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
169
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
170
+ [[NSURLCache sharedURLCache] setMemoryCapacity:0];
171
+ [[NSURLCache sharedURLCache]
172
+ setMemoryCapacity:10 * 1024 * 1024]; // 10MB
173
+ });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ - (void)restartRealtimeCapture {
180
+ if ([self.internalCaptureMode isEqualToString:@"realtime"]) {
181
+ [self stopCapture];
182
+ [self startRealtimeCapture];
183
+ }
184
+ }
185
+
186
+ #pragma mark - Core Capture Logic - Align with Android's performCapture
187
+
188
+ - (void)performCapture:(NSString *)trigger {
189
+ if (self.isCapturing || self.isDestroyed)
190
+ return;
191
+
192
+ // Throttle captures to prevent excessive calls
193
+ CFTimeInterval currentTime = CACurrentMediaTime();
194
+ if (currentTime - self.lastCaptureTime < 1.0 / 30.0) { // Max 30fps
195
+ return;
196
+ }
197
+ self.lastCaptureTime = currentTime;
198
+
199
+ self.isCapturing = YES;
200
+
201
+ @try {
202
+ CVPixelBufferRef pixelBuffer = [self createPixelBuffer];
203
+ if (pixelBuffer) {
204
+ [self processPixelBuffer:pixelBuffer trigger:trigger];
205
+ CVPixelBufferRelease(pixelBuffer);
206
+ }
207
+ } @finally {
208
+ self.isCapturing = NO;
209
+ }
210
+ }
211
+
212
+ - (UIImage *)createBitmap {
213
+ CGSize viewSize = self.bounds.size;
214
+
215
+ // Check for valid bounds
216
+ if (viewSize.width <= 0 || viewSize.height <= 0) {
217
+ return nil;
218
+ }
219
+
220
+ // Limit maximum image dimensions to prevent memory issues
221
+ CGFloat maxDimension = 1920.0; // Max 1920px on either side
222
+ CGFloat scale = [UIScreen mainScreen].scale;
223
+
224
+ // Calculate actual pixel dimensions
225
+ CGFloat pixelWidth = viewSize.width * scale;
226
+ CGFloat pixelHeight = viewSize.height * scale;
227
+
228
+ // Scale down if too large
229
+ if (pixelWidth > maxDimension || pixelHeight > maxDimension) {
230
+ CGFloat scaleDown =
231
+ MIN(maxDimension / pixelWidth, maxDimension / pixelHeight);
232
+ scale = scale * scaleDown;
233
+ }
234
+
235
+ // Further limit scale on very large views
236
+ if (viewSize.width * viewSize.height > 1000000) { // > 1M points
237
+ scale = MIN(scale, 1.0);
238
+ }
239
+
240
+ @autoreleasepool {
241
+ UIGraphicsBeginImageContextWithOptions(viewSize, NO, scale);
242
+
243
+ CGContextRef context = UIGraphicsGetCurrentContext();
244
+ if (!context) {
245
+ UIGraphicsEndImageContext();
246
+ return nil;
247
+ }
248
+
249
+ [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
250
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
251
+ UIGraphicsEndImageContext();
252
+
253
+ return image;
254
+ }
255
+ }
256
+
257
+ #pragma mark - Public Methods - Align with Android interface
258
+
259
+ // Align with Android's performManualCapture
260
+ - (void)performCaptureWithTrigger:(NSString *)trigger {
261
+ [self performCapture:trigger];
262
+ }
263
+
264
+ // Align with Android's setBitmapCaptureCallback
265
+ - (void)setBitmapCaptureCallback:(id<VeLiveBitmapCaptureCallback>)callback {
266
+ _bitmapCaptureCallback = callback;
267
+ }
268
+
269
+ #pragma mark - Cleanup - Align with Android
270
+
271
+ - (void)cleanup {
272
+ self.isDestroyed = YES;
273
+ [self stopCapture];
274
+
275
+ // Clean up pixelBuffer cache
276
+ if (self.lastPixelBuffer) {
277
+ CVPixelBufferRelease(self.lastPixelBuffer);
278
+ self.lastPixelBuffer = NULL;
279
+ }
280
+
281
+ // Clean up callback references, avoid circular references
282
+ self.bitmapCaptureCallback = nil;
283
+ }
284
+
285
+ - (void)dealloc {
286
+ // Remove from global cache
287
+ if (self.viewId) {
288
+ NSMutableDictionary *cachedViews = [VeLiveMixerHelper cachedMixedViews];
289
+ if (cachedViews) {
290
+ [cachedViews removeObjectForKey:self.viewId];
291
+ }
292
+ }
293
+ }
294
+
295
+ #pragma mark - View Conversion Methods
296
+
297
+ - (UIImage *)snapshotIncludeSubviews {
298
+ CGSize viewSize = self.bounds.size;
299
+
300
+ // Check for valid bounds
301
+ if (viewSize.width <= 0 || viewSize.height <= 0) {
302
+ return nil;
303
+ }
304
+
305
+ // Limit maximum image dimensions to prevent memory issues
306
+ CGFloat maxDimension = 1920.0; // Max 1920px on either side
307
+ CGFloat scale = [UIScreen mainScreen].scale;
308
+
309
+ // Calculate actual pixel dimensions
310
+ CGFloat pixelWidth = viewSize.width * scale;
311
+ CGFloat pixelHeight = viewSize.height * scale;
312
+
313
+ // Scale down if too large
314
+ if (pixelWidth > maxDimension || pixelHeight > maxDimension) {
315
+ CGFloat scaleDown =
316
+ MIN(maxDimension / pixelWidth, maxDimension / pixelHeight);
317
+ scale = scale * scaleDown;
318
+ }
319
+
320
+ // Further limit scale on very large views
321
+ if (viewSize.width * viewSize.height > 1000000) { // > 1M points
322
+ scale = MIN(scale, 1.0);
323
+ }
324
+
325
+ @autoreleasepool {
326
+ UIGraphicsBeginImageContextWithOptions(viewSize, NO, scale);
327
+
328
+ CGContextRef context = UIGraphicsGetCurrentContext();
329
+ if (!context) {
330
+ UIGraphicsEndImageContext();
331
+ return nil;
332
+ }
333
+
334
+ [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
335
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
336
+ UIGraphicsEndImageContext();
337
+
338
+ return image;
339
+ }
340
+ }
341
+
342
+ - (CVPixelBufferRef)createPixelBuffer {
343
+ UIImage *bitmap = [self createBitmap];
344
+ if (!bitmap) {
345
+ return NULL;
346
+ }
347
+
348
+ return [self pixelBufferFromUIImage:bitmap];
349
+ }
350
+
351
+ - (CMSampleBufferRef)createSampleBuffer {
352
+ // Implement sample buffer creation (corresponds to Android's VideoFrame)
353
+ return NULL; // Temporary return NULL, implementation based on needs
354
+ }
355
+
356
+ #pragma mark - React Native Child View Support - Focus on RN subview management
357
+
358
+ // React Native subview insertion - This is the main method called by RN
359
+ // framework
360
+ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex {
361
+ // Process directly on main thread because React Native ensures thread safety
362
+ [super insertSubview:subview atIndex:atIndex];
363
+ [self setSubViewAutoLayout:subview];
364
+ }
365
+
366
+ - (void)removeReactSubview:(UIView *)subview {
367
+ [subview removeFromSuperview];
368
+ }
369
+
370
+ - (NSArray<UIView *> *)reactSubviews {
371
+ return [self subviews];
372
+ }
373
+
374
+ // Set Auto Layout constraints - Ensure subview fills parent view
375
+ - (void)setSubViewAutoLayout:(UIView *)subview {
376
+ // Disable automatic conversion, use constraints layout
377
+ subview.translatesAutoresizingMaskIntoConstraints = NO;
378
+
379
+ // Set four-side constraints, let subview fill parent view
380
+ [NSLayoutConstraint activateConstraints:@[
381
+ [subview.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
382
+ [subview.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
383
+ [subview.topAnchor constraintEqualToAnchor:self.topAnchor],
384
+ [subview.bottomAnchor constraintEqualToAnchor:self.bottomAnchor]
385
+ ]];
386
+ }
387
+
388
+ // Override layoutSubviews to ensure correct subview layout
389
+ - (void)layoutSubviews {
390
+ [super layoutSubviews];
391
+ if (self.isDestroyed)
392
+ return;
393
+
394
+ // Original capture logic
395
+ if ([self.internalCaptureMode isEqualToString:@"onchange"]) {
396
+ [self performCapture:@"onchange"];
397
+ }
398
+ }
399
+
400
+ // Ensure UIView can correctly display background color and subviews
401
+ - (void)setBackgroundColor:(UIColor *)backgroundColor {
402
+ [super setBackgroundColor:backgroundColor];
403
+ }
404
+
405
+ #pragma mark - Helper Methods
406
+
407
+ - (CVPixelBufferRef)pixelBufferFromUIImage:(UIImage *)image {
408
+ if (!image) {
409
+ return NULL;
410
+ }
411
+
412
+ CGImageRef cgImage = image.CGImage;
413
+ if (!cgImage) {
414
+ return NULL;
415
+ }
416
+
417
+ CGFloat width = CGImageGetWidth(cgImage);
418
+ CGFloat height = CGImageGetHeight(cgImage);
419
+
420
+ NSDictionary *options = @{
421
+ (NSString *)kCVPixelBufferCGImageCompatibilityKey : @YES,
422
+ (NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
423
+ (NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{}
424
+ };
425
+
426
+ CVPixelBufferRef pixelBuffer = NULL;
427
+ CVReturn status = CVPixelBufferCreate(
428
+ kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA,
429
+ (__bridge CFDictionaryRef)options, &pixelBuffer);
430
+
431
+ if (status != kCVReturnSuccess || !pixelBuffer) {
432
+ return NULL;
433
+ }
434
+
435
+ CVPixelBufferLockBaseAddress(pixelBuffer, 0);
436
+
437
+ void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);
438
+ size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
439
+
440
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
441
+ CGContextRef context = CGBitmapContextCreate(
442
+ pixelData, width, height, 8, bytesPerRow, colorSpace,
443
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
444
+
445
+ if (!context) {
446
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
447
+ CVPixelBufferRelease(pixelBuffer);
448
+ CGColorSpaceRelease(colorSpace);
449
+ return NULL;
450
+ }
451
+
452
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
453
+
454
+ CGContextRelease(context);
455
+ CGColorSpaceRelease(colorSpace);
456
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
457
+
458
+ return pixelBuffer;
459
+ }
460
+
461
+ - (void)processPixelBuffer:(CVPixelBufferRef)pixelBuffer
462
+ trigger:(NSString *)trigger {
463
+ @autoreleasepool {
464
+ if (!pixelBuffer) {
465
+ return;
466
+ }
467
+
468
+ // Clean up old pixelBuffer cache
469
+ if (self.lastPixelBuffer) {
470
+ CVPixelBufferRelease(self.lastPixelBuffer);
471
+ self.lastPixelBuffer = NULL;
472
+ }
473
+
474
+ // Cache pixelBuffer for non-realtime modes
475
+ if (![trigger isEqualToString:@"realtime"]) {
476
+ // Retain current pixelBuffer as cache
477
+ CVPixelBufferRetain(pixelBuffer);
478
+ self.lastPixelBuffer = pixelBuffer;
479
+ }
480
+
481
+ // If callback is needed, create temporary UIImage
482
+ if (self.bitmapCaptureCallback || self.onBitmapCapture) {
483
+ UIImage *bitmap = [self UIImageFromPixelBuffer:pixelBuffer];
484
+ if (bitmap) {
485
+ if (self.bitmapCaptureCallback) {
486
+ @try {
487
+ [self.bitmapCaptureCallback onBitmapCaptured:bitmap];
488
+ } @catch (NSException *exception) {
489
+ if (self.bitmapCaptureCallback) {
490
+ [self.bitmapCaptureCallback
491
+ onCaptureError:[NSString
492
+ stringWithFormat:@"Callback error: %@",
493
+ exception.reason]];
494
+ }
495
+ }
496
+ }
497
+
498
+ if (self.onBitmapCapture) {
499
+ @autoreleasepool {
500
+ NSDictionary *event = @{
501
+ @"trigger" : trigger ?: @"unknown",
502
+ @"success" : @(YES),
503
+ @"bitmapWidth" : @(bitmap.size.width),
504
+ @"bitmapHeight" : @(bitmap.size.height)
505
+ };
506
+ self.onBitmapCapture(event);
507
+ }
508
+ }
509
+ }
510
+ }
511
+
512
+ // Memory cleanup for realtime mode
513
+ if ([trigger isEqualToString:@"realtime"]) {
514
+ static int realtimeCount = 0;
515
+ realtimeCount++;
516
+ if (realtimeCount % 10 == 0) {
517
+ dispatch_async(
518
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
519
+ @autoreleasepool {
520
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
521
+ }
522
+ });
523
+ }
524
+ }
525
+ }
526
+ }
527
+
528
+ - (UIImage *)UIImageFromPixelBuffer:(CVPixelBufferRef)pixelBuffer {
529
+ if (!pixelBuffer) {
530
+ return nil;
531
+ }
532
+
533
+ CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
534
+ CIContext *context = [CIContext contextWithOptions:nil];
535
+ CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
536
+
537
+ if (!cgImage) {
538
+ return nil;
539
+ }
540
+
541
+ UIImage *image = [UIImage imageWithCGImage:cgImage];
542
+ CGImageRelease(cgImage);
543
+
544
+ return image;
545
+ }
546
+
547
+ @end
@@ -0,0 +1,56 @@
1
+ //
2
+ // VeLiveMixerViewManager.m
3
+ // react-native-live-push
4
+ //
5
+ // Created to manage VeLiveMixerView React Native component
6
+ //
7
+
8
+ #import "VeLiveMixerView.h"
9
+ #import <Foundation/Foundation.h>
10
+ #import <React/RCTComponent.h>
11
+ #import <React/RCTViewManager.h>
12
+ #import <UIKit/UIKit.h>
13
+
14
+ @interface VeLiveMixerViewManager : RCTViewManager
15
+ @end
16
+
17
+ @implementation VeLiveMixerViewManager
18
+
19
+ + (BOOL)requiresMainQueueSetup {
20
+ return YES;
21
+ }
22
+
23
+ RCT_EXPORT_MODULE(VeLiveMixView)
24
+
25
+ - (VeLiveMixerUIView *)view {
26
+ VeLiveMixerUIView *view = [[VeLiveMixerUIView alloc] init];
27
+ view.accessibilityLabel = @"VeLiveMixerUIView";
28
+ return view;
29
+ }
30
+
31
+ // 导出视图ID属性 - 使用自定义属性处理
32
+ RCT_CUSTOM_VIEW_PROPERTY(viewId, NSString, VeLiveMixerUIView) {
33
+ if (json == nil) {
34
+ return;
35
+ }
36
+
37
+ NSString *viewId = [RCTConvert NSString:json];
38
+ [view setViewId:viewId];
39
+ }
40
+
41
+ // 导出捕获配置属性
42
+ RCT_CUSTOM_VIEW_PROPERTY(captureMode, NSString, VeLiveMixerUIView) {
43
+ NSString *captureMode = [RCTConvert NSString:json];
44
+ if (captureMode != nil) {
45
+ [view setCaptureMode:captureMode];
46
+ } else {
47
+ [view setCaptureMode:@"onchange"];
48
+ }
49
+ }
50
+
51
+ RCT_CUSTOM_VIEW_PROPERTY(captureFramerate, NSNumber, VeLiveMixerUIView) {
52
+ NSNumber *framerate = [RCTConvert NSNumber:json];
53
+ [view setCaptureFramerate:framerate];
54
+ }
55
+
56
+ @end