@byteplus/react-native-live-push 1.1.3-rc.4 → 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.
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/volcengine/velive/rn/push/ClassHelper.java +9 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/NativeVariableManager.java +5 -8
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushModule.java +1 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +2 -4
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +50 -145
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerViewManager.java +1 -37
- package/ios/VeLiveMixerHelper.h +16 -11
- package/ios/VeLiveMixerHelper.m +39 -13
- package/ios/VeLiveMixerView.h +1 -8
- package/ios/VeLiveMixerView.m +178 -231
- package/ios/VeLiveMixerViewManager.m +2 -38
- package/lib/commonjs/index.js +23205 -20306
- package/lib/commonjs/typescript/android/index.d.ts +0 -3
- package/lib/commonjs/typescript/codegen/android/api.d.ts +190 -644
- package/lib/commonjs/typescript/codegen/android/callback.d.ts +234 -2
- package/lib/commonjs/typescript/codegen/android/errorcode.d.ts +66 -0
- package/lib/commonjs/typescript/codegen/android/keytype.d.ts +1014 -181
- package/lib/commonjs/typescript/codegen/ios/api.d.ts +890 -0
- package/lib/commonjs/typescript/codegen/ios/callback.d.ts +162 -0
- package/lib/commonjs/typescript/codegen/ios/errorcode.d.ts +101 -1
- package/lib/commonjs/typescript/codegen/ios/keytype.d.ts +694 -0
- package/lib/commonjs/typescript/codegen/pack/api.d.ts +303 -686
- package/lib/commonjs/typescript/codegen/pack/callback.d.ts +37 -38
- package/lib/commonjs/typescript/codegen/pack/errorcode.d.ts +75 -5
- package/lib/commonjs/typescript/codegen/pack/keytype.d.ts +1002 -298
- package/lib/commonjs/typescript/core/api.d.ts +2 -1
- package/lib/commonjs/typescript/core/keytype.d.ts +2 -3
- package/lib/commonjs/typescript/core/pusher.d.ts +0 -3
- package/lib/commonjs/typescript/view/MixView.d.ts +1 -9
- package/lib/module/index.js +23206 -20306
- package/lib/module/typescript/android/index.d.ts +0 -3
- package/lib/module/typescript/codegen/android/api.d.ts +190 -644
- package/lib/module/typescript/codegen/android/callback.d.ts +234 -2
- package/lib/module/typescript/codegen/android/errorcode.d.ts +66 -0
- package/lib/module/typescript/codegen/android/keytype.d.ts +1014 -181
- package/lib/module/typescript/codegen/ios/api.d.ts +890 -0
- package/lib/module/typescript/codegen/ios/callback.d.ts +162 -0
- package/lib/module/typescript/codegen/ios/errorcode.d.ts +101 -1
- package/lib/module/typescript/codegen/ios/keytype.d.ts +694 -0
- package/lib/module/typescript/codegen/pack/api.d.ts +303 -686
- package/lib/module/typescript/codegen/pack/callback.d.ts +37 -38
- package/lib/module/typescript/codegen/pack/errorcode.d.ts +75 -5
- package/lib/module/typescript/codegen/pack/keytype.d.ts +1002 -298
- package/lib/module/typescript/core/api.d.ts +2 -1
- package/lib/module/typescript/core/keytype.d.ts +2 -3
- package/lib/module/typescript/core/pusher.d.ts +0 -3
- package/lib/module/typescript/view/MixView.d.ts +1 -9
- package/lib/typescript/android/index.d.ts +0 -3
- package/lib/typescript/codegen/android/api.d.ts +190 -644
- package/lib/typescript/codegen/android/callback.d.ts +234 -2
- package/lib/typescript/codegen/android/errorcode.d.ts +66 -0
- package/lib/typescript/codegen/android/keytype.d.ts +1014 -181
- package/lib/typescript/codegen/ios/api.d.ts +890 -0
- package/lib/typescript/codegen/ios/callback.d.ts +162 -0
- package/lib/typescript/codegen/ios/errorcode.d.ts +101 -1
- package/lib/typescript/codegen/ios/keytype.d.ts +694 -0
- package/lib/typescript/codegen/pack/api.d.ts +303 -686
- package/lib/typescript/codegen/pack/callback.d.ts +37 -38
- package/lib/typescript/codegen/pack/errorcode.d.ts +75 -5
- package/lib/typescript/codegen/pack/keytype.d.ts +1002 -298
- package/lib/typescript/core/api.d.ts +2 -1
- package/lib/typescript/core/keytype.d.ts +2 -3
- package/lib/typescript/core/pusher.d.ts +0 -3
- package/lib/typescript/view/MixView.d.ts +1 -9
- package/package.json +1 -1
- package/react-native-velive-push.podspec +3 -3
- package/android/src/main/java/com/volcengine/velive/rn/push/ScreenCaptureHelper.java +0 -73
package/ios/VeLiveMixerView.m
CHANGED
|
@@ -8,24 +8,16 @@
|
|
|
8
8
|
#import "VeLiveMixerView.h"
|
|
9
9
|
#import "VeLiveMixerHelper.h"
|
|
10
10
|
#import <AVFoundation/AVFoundation.h>
|
|
11
|
+
#import <CoreImage/CoreImage.h>
|
|
11
12
|
#import <CoreMedia/CoreMedia.h>
|
|
12
13
|
#import <CoreVideo/CoreVideo.h>
|
|
13
14
|
#import <UIKit/UIKit.h>
|
|
14
15
|
|
|
15
16
|
@interface VeLiveMixerUIView ()
|
|
16
17
|
|
|
17
|
-
// Mix configuration - Align with Android
|
|
18
|
-
@property(nonatomic, assign) float mixX;
|
|
19
|
-
@property(nonatomic, assign) float mixY;
|
|
20
|
-
@property(nonatomic, assign) float mixWidth;
|
|
21
|
-
@property(nonatomic, assign) float mixHeight;
|
|
22
|
-
@property(nonatomic, assign) int mixZOrder;
|
|
23
|
-
@property(nonatomic, assign) int mixRenderMode;
|
|
24
|
-
|
|
25
18
|
// Capture configuration - Internal properties
|
|
26
19
|
@property(nonatomic, strong) NSString *internalCaptureMode;
|
|
27
20
|
@property(nonatomic, assign) float internalCaptureFramerate;
|
|
28
|
-
@property(nonatomic, assign) float internalAutoSensitivity;
|
|
29
21
|
|
|
30
22
|
// Capture state - Align with Android
|
|
31
23
|
@property(nonatomic, strong) NSTimer *captureTimer;
|
|
@@ -34,21 +26,12 @@
|
|
|
34
26
|
@property(nonatomic, assign) CFTimeInterval lastCaptureTime;
|
|
35
27
|
|
|
36
28
|
// Performance optimization - Align with Android
|
|
37
|
-
@property(nonatomic
|
|
38
|
-
|
|
39
|
-
// Auto mode state - Align with Android
|
|
40
|
-
@property(nonatomic, assign) int changeCount;
|
|
41
|
-
@property(nonatomic, assign) CFTimeInterval windowStartTime;
|
|
42
|
-
@property(nonatomic, assign) BOOL isInRealtimeMode;
|
|
29
|
+
@property(nonatomic) CVPixelBufferRef lastPixelBuffer;
|
|
43
30
|
|
|
44
31
|
@end
|
|
45
32
|
|
|
46
33
|
@implementation VeLiveMixerUIView
|
|
47
34
|
|
|
48
|
-
static const CFTimeInterval AUTO_WINDOW_SECONDS = 2.0; // 2 second window
|
|
49
|
-
static const int HIGH_CHANGE_THRESHOLD = 10;
|
|
50
|
-
static const int LOW_CHANGE_THRESHOLD = 2;
|
|
51
|
-
|
|
52
35
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|
53
36
|
self = [super initWithFrame:frame];
|
|
54
37
|
if (self) {
|
|
@@ -69,25 +52,13 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
69
52
|
|
|
70
53
|
- (void)initializeCapture {
|
|
71
54
|
// Initialize defaults - Completely align with Android
|
|
72
|
-
_mixX = 0.0f;
|
|
73
|
-
_mixY = 0.0f;
|
|
74
|
-
_mixWidth = 0.0f;
|
|
75
|
-
_mixHeight = 0.0f;
|
|
76
|
-
_mixZOrder = 0;
|
|
77
|
-
_mixRenderMode = 0;
|
|
78
|
-
|
|
79
55
|
_internalCaptureMode = @"onchange";
|
|
80
|
-
_internalCaptureFramerate =
|
|
81
|
-
_internalAutoSensitivity = 5.0f;
|
|
56
|
+
_internalCaptureFramerate = 5.0f; // realTime mode default 5 fps
|
|
82
57
|
|
|
83
58
|
_isCapturing = NO;
|
|
84
59
|
_isDestroyed = NO;
|
|
85
60
|
_lastCaptureTime = 0;
|
|
86
|
-
|
|
87
|
-
// Auto mode state
|
|
88
|
-
_changeCount = 0;
|
|
89
|
-
_windowStartTime = 0;
|
|
90
|
-
_isInRealtimeMode = NO;
|
|
61
|
+
_lastPixelBuffer = NULL;
|
|
91
62
|
}
|
|
92
63
|
|
|
93
64
|
#pragma mark - Property Getters/Setters - Align with React Native props
|
|
@@ -116,14 +87,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
116
87
|
}
|
|
117
88
|
}
|
|
118
89
|
|
|
119
|
-
- (NSNumber *)autoSensitivity {
|
|
120
|
-
return @(self.internalAutoSensitivity);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
- (void)setAutoSensitivity:(NSNumber *)sensitivity {
|
|
124
|
-
_internalAutoSensitivity = MAX(1.0f, MIN(10.0f, [sensitivity floatValue]));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
90
|
- (void)setViewId:(NSString *)viewId {
|
|
128
91
|
if (!viewId) {
|
|
129
92
|
return;
|
|
@@ -142,61 +105,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
142
105
|
}
|
|
143
106
|
}
|
|
144
107
|
|
|
145
|
-
#pragma mark - Property setters for React Native props - Align with Android
|
|
146
|
-
|
|
147
|
-
// React Native property setter - Corresponds to VeLiveMixerViewManager.m
|
|
148
|
-
// properties
|
|
149
|
-
- (void)setX:(NSNumber *)x {
|
|
150
|
-
_x = x;
|
|
151
|
-
_mixX = [x floatValue];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
- (void)setY:(NSNumber *)y {
|
|
155
|
-
_y = y;
|
|
156
|
-
_mixY = [y floatValue];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
- (void)setWidth:(NSNumber *)width {
|
|
160
|
-
_width = width;
|
|
161
|
-
_mixWidth = [width floatValue];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
- (void)setHeight:(NSNumber *)height {
|
|
165
|
-
_height = height;
|
|
166
|
-
_mixHeight = [height floatValue];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
- (void)setZOrder:(NSNumber *)zOrder {
|
|
170
|
-
_zOrder = zOrder;
|
|
171
|
-
_mixZOrder = [zOrder intValue];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
- (void)setRenderMode:(NSNumber *)renderMode {
|
|
175
|
-
_renderMode = renderMode;
|
|
176
|
-
_mixRenderMode = [renderMode intValue];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
#pragma mark - Internal setters for mix configuration - Internal use
|
|
180
|
-
|
|
181
|
-
- (void)setMixX:(float)x {
|
|
182
|
-
_mixX = x;
|
|
183
|
-
}
|
|
184
|
-
- (void)setMixY:(float)y {
|
|
185
|
-
_mixY = y;
|
|
186
|
-
}
|
|
187
|
-
- (void)setMixWidth:(float)width {
|
|
188
|
-
_mixWidth = width;
|
|
189
|
-
}
|
|
190
|
-
- (void)setMixHeight:(float)height {
|
|
191
|
-
_mixHeight = height;
|
|
192
|
-
}
|
|
193
|
-
- (void)setMixZOrder:(int)zOrder {
|
|
194
|
-
_mixZOrder = zOrder;
|
|
195
|
-
}
|
|
196
|
-
- (void)setMixRenderMode:(int)renderMode {
|
|
197
|
-
_mixRenderMode = renderMode;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
108
|
#pragma mark - Capture Methods - Completely align with Android
|
|
201
109
|
|
|
202
110
|
- (void)startCapture {
|
|
@@ -207,8 +115,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
207
115
|
[self startOnChangeCapture];
|
|
208
116
|
} else if ([self.internalCaptureMode isEqualToString:@"realtime"]) {
|
|
209
117
|
[self startRealtimeCapture];
|
|
210
|
-
} else if ([self.internalCaptureMode isEqualToString:@"auto"]) {
|
|
211
|
-
[self startAutoCapture];
|
|
212
118
|
}
|
|
213
119
|
// manual mode doesn't auto-start
|
|
214
120
|
}
|
|
@@ -220,7 +126,22 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
220
126
|
}
|
|
221
127
|
|
|
222
128
|
- (void)startOnChangeCapture {
|
|
223
|
-
// onchange mode triggers
|
|
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
|
+
}
|
|
224
145
|
}
|
|
225
146
|
|
|
226
147
|
- (void)startRealtimeCapture {
|
|
@@ -262,59 +183,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
262
183
|
}
|
|
263
184
|
}
|
|
264
185
|
|
|
265
|
-
- (void)startAutoCapture {
|
|
266
|
-
// Start with onchange mode
|
|
267
|
-
self.isInRealtimeMode = NO;
|
|
268
|
-
self.windowStartTime = CACurrentMediaTime();
|
|
269
|
-
self.changeCount = 0;
|
|
270
|
-
[self startOnChangeCapture];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
#pragma mark - Layout Change Detection - Align with Android's onViewChanged
|
|
274
|
-
|
|
275
|
-
- (void)handleAutoModeChange {
|
|
276
|
-
if (!self.isInRealtimeMode) {
|
|
277
|
-
[self performCapture:@"auto_onchange"];
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Count changes for auto mode
|
|
281
|
-
self.changeCount++;
|
|
282
|
-
|
|
283
|
-
CFTimeInterval currentTime = CACurrentMediaTime();
|
|
284
|
-
if (currentTime - self.windowStartTime >= AUTO_WINDOW_SECONDS) {
|
|
285
|
-
[self evaluateAutoMode];
|
|
286
|
-
// Reset window
|
|
287
|
-
self.windowStartTime = currentTime;
|
|
288
|
-
self.changeCount = 0;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
- (void)evaluateAutoMode {
|
|
293
|
-
float changesPerSecond = self.changeCount / AUTO_WINDOW_SECONDS;
|
|
294
|
-
float threshold = self.internalAutoSensitivity;
|
|
295
|
-
|
|
296
|
-
BOOL shouldBeRealtime =
|
|
297
|
-
changesPerSecond > (HIGH_CHANGE_THRESHOLD / threshold);
|
|
298
|
-
BOOL shouldBeOnChange = changesPerSecond < (LOW_CHANGE_THRESHOLD / threshold);
|
|
299
|
-
|
|
300
|
-
if (shouldBeRealtime && !self.isInRealtimeMode) {
|
|
301
|
-
[self switchToRealtimeMode];
|
|
302
|
-
} else if (shouldBeOnChange && self.isInRealtimeMode) {
|
|
303
|
-
[self switchToOnChangeMode];
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
- (void)switchToRealtimeMode {
|
|
308
|
-
self.isInRealtimeMode = YES;
|
|
309
|
-
[self startRealtimeCapture];
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
- (void)switchToOnChangeMode {
|
|
313
|
-
self.isInRealtimeMode = NO;
|
|
314
|
-
[self.captureTimer invalidate];
|
|
315
|
-
self.captureTimer = nil;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
186
|
#pragma mark - Core Capture Logic - Align with Android's performCapture
|
|
319
187
|
|
|
320
188
|
- (void)performCapture:(NSString *)trigger {
|
|
@@ -323,7 +191,7 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
323
191
|
|
|
324
192
|
// Throttle captures to prevent excessive calls
|
|
325
193
|
CFTimeInterval currentTime = CACurrentMediaTime();
|
|
326
|
-
if (currentTime - self.lastCaptureTime < 1.0 /
|
|
194
|
+
if (currentTime - self.lastCaptureTime < 1.0 / 30.0) { // Max 30fps
|
|
327
195
|
return;
|
|
328
196
|
}
|
|
329
197
|
self.lastCaptureTime = currentTime;
|
|
@@ -331,9 +199,10 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
331
199
|
self.isCapturing = YES;
|
|
332
200
|
|
|
333
201
|
@try {
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
[self
|
|
202
|
+
CVPixelBufferRef pixelBuffer = [self createPixelBuffer];
|
|
203
|
+
if (pixelBuffer) {
|
|
204
|
+
[self processPixelBuffer:pixelBuffer trigger:trigger];
|
|
205
|
+
CVPixelBufferRelease(pixelBuffer);
|
|
337
206
|
}
|
|
338
207
|
} @finally {
|
|
339
208
|
self.isCapturing = NO;
|
|
@@ -385,75 +254,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
385
254
|
}
|
|
386
255
|
}
|
|
387
256
|
|
|
388
|
-
- (void)processBitmap:(UIImage *)bitmap trigger:(NSString *)trigger {
|
|
389
|
-
@autoreleasepool {
|
|
390
|
-
if (!bitmap || bitmap.size.width <= 0 || bitmap.size.height <= 0) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Check bitmap memory size and skip if too large
|
|
395
|
-
CGFloat bitmapMemoryMB =
|
|
396
|
-
(bitmap.size.width * bitmap.size.height * 4.0) / (1024.0 * 1024.0);
|
|
397
|
-
if (bitmapMemoryMB > 50.0) { // Skip bitmaps larger than 50MB
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (self.bitmapCaptureCallback) {
|
|
402
|
-
@try {
|
|
403
|
-
[self.bitmapCaptureCallback onBitmapCaptured:bitmap];
|
|
404
|
-
} @catch (NSException *exception) {
|
|
405
|
-
if (self.bitmapCaptureCallback) {
|
|
406
|
-
[self.bitmapCaptureCallback
|
|
407
|
-
onCaptureError:[NSString stringWithFormat:@"Callback error: %@",
|
|
408
|
-
exception.reason]];
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (self.onBitmapCapture) {
|
|
414
|
-
@autoreleasepool {
|
|
415
|
-
NSDictionary *event = @{
|
|
416
|
-
@"trigger" : trigger ?: @"unknown",
|
|
417
|
-
@"success" : @(YES),
|
|
418
|
-
@"x" : @(self.mixX),
|
|
419
|
-
@"y" : @(self.mixY),
|
|
420
|
-
@"width" : @(self.mixWidth),
|
|
421
|
-
@"height" : @(self.mixHeight),
|
|
422
|
-
@"zOrder" : @(self.mixZOrder),
|
|
423
|
-
@"renderMode" : @(self.mixRenderMode),
|
|
424
|
-
@"bitmapWidth" : @(bitmap.size.width),
|
|
425
|
-
@"bitmapHeight" : @(bitmap.size.height)
|
|
426
|
-
};
|
|
427
|
-
self.onBitmapCapture(event);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Clean up old bitmap immediately
|
|
432
|
-
if (self.lastBitmap) {
|
|
433
|
-
self.lastBitmap = nil;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// For realtime mode, don't keep bitmap reference to reduce memory usage
|
|
437
|
-
if ([trigger isEqualToString:@"realtime"]) {
|
|
438
|
-
// Bitmap will be auto-released at autoreleasepool end
|
|
439
|
-
|
|
440
|
-
// Force memory cleanup every 10 realtime captures
|
|
441
|
-
static int realtimeCount = 0;
|
|
442
|
-
realtimeCount++;
|
|
443
|
-
if (realtimeCount % 10 == 0) {
|
|
444
|
-
dispatch_async(
|
|
445
|
-
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
|
446
|
-
@autoreleasepool {
|
|
447
|
-
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
} else {
|
|
452
|
-
self.lastBitmap = bitmap;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
257
|
#pragma mark - Public Methods - Align with Android interface
|
|
458
258
|
|
|
459
259
|
// Align with Android's performManualCapture
|
|
@@ -472,8 +272,11 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
472
272
|
self.isDestroyed = YES;
|
|
473
273
|
[self stopCapture];
|
|
474
274
|
|
|
475
|
-
// Clean up
|
|
476
|
-
self.
|
|
275
|
+
// Clean up pixelBuffer cache
|
|
276
|
+
if (self.lastPixelBuffer) {
|
|
277
|
+
CVPixelBufferRelease(self.lastPixelBuffer);
|
|
278
|
+
self.lastPixelBuffer = NULL;
|
|
279
|
+
}
|
|
477
280
|
|
|
478
281
|
// Clean up callback references, avoid circular references
|
|
479
282
|
self.bitmapCaptureCallback = nil;
|
|
@@ -537,8 +340,12 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
537
340
|
}
|
|
538
341
|
|
|
539
342
|
- (CVPixelBufferRef)createPixelBuffer {
|
|
540
|
-
|
|
541
|
-
|
|
343
|
+
UIImage *bitmap = [self createBitmap];
|
|
344
|
+
if (!bitmap) {
|
|
345
|
+
return NULL;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return [self pixelBufferFromUIImage:bitmap];
|
|
542
349
|
}
|
|
543
350
|
|
|
544
351
|
- (CMSampleBufferRef)createSampleBuffer {
|
|
@@ -587,8 +394,6 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
587
394
|
// Original capture logic
|
|
588
395
|
if ([self.internalCaptureMode isEqualToString:@"onchange"]) {
|
|
589
396
|
[self performCapture:@"onchange"];
|
|
590
|
-
} else if ([self.internalCaptureMode isEqualToString:@"auto"]) {
|
|
591
|
-
[self handleAutoModeChange];
|
|
592
397
|
}
|
|
593
398
|
}
|
|
594
399
|
|
|
@@ -597,4 +402,146 @@ static const int LOW_CHANGE_THRESHOLD = 2;
|
|
|
597
402
|
[super setBackgroundColor:backgroundColor];
|
|
598
403
|
}
|
|
599
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
|
+
|
|
600
547
|
@end
|
|
@@ -33,42 +33,11 @@ RCT_CUSTOM_VIEW_PROPERTY(viewId, NSString, VeLiveMixerUIView) {
|
|
|
33
33
|
if (json == nil) {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
NSString *viewId = [RCTConvert NSString:json];
|
|
38
38
|
[view setViewId:viewId];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// 导出混合配置属性 - 与Android保持一致的属性名
|
|
42
|
-
RCT_CUSTOM_VIEW_PROPERTY(x, NSNumber, VeLiveMixerUIView) {
|
|
43
|
-
NSNumber *x = [RCTConvert NSNumber:json];
|
|
44
|
-
[view setX:x];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
RCT_CUSTOM_VIEW_PROPERTY(y, NSNumber, VeLiveMixerUIView) {
|
|
48
|
-
NSNumber *y = [RCTConvert NSNumber:json];
|
|
49
|
-
[view setY:y];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
RCT_CUSTOM_VIEW_PROPERTY(width, NSNumber, VeLiveMixerUIView) {
|
|
53
|
-
NSNumber *width = [RCTConvert NSNumber:json];
|
|
54
|
-
[view setWidth:width];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
RCT_CUSTOM_VIEW_PROPERTY(height, NSNumber, VeLiveMixerUIView) {
|
|
58
|
-
NSNumber *height = [RCTConvert NSNumber:json];
|
|
59
|
-
[view setHeight:height];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
RCT_CUSTOM_VIEW_PROPERTY(zOrder, NSNumber, VeLiveMixerUIView) {
|
|
63
|
-
NSNumber *zOrder = [RCTConvert NSNumber:json];
|
|
64
|
-
[view setZOrder:zOrder];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
RCT_CUSTOM_VIEW_PROPERTY(renderMode, NSNumber, VeLiveMixerUIView) {
|
|
68
|
-
NSNumber *renderMode = [RCTConvert NSNumber:json];
|
|
69
|
-
[view setRenderMode:renderMode];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
41
|
// 导出捕获配置属性
|
|
73
42
|
RCT_CUSTOM_VIEW_PROPERTY(captureMode, NSString, VeLiveMixerUIView) {
|
|
74
43
|
NSString *captureMode = [RCTConvert NSString:json];
|
|
@@ -84,9 +53,4 @@ RCT_CUSTOM_VIEW_PROPERTY(captureFramerate, NSNumber, VeLiveMixerUIView) {
|
|
|
84
53
|
[view setCaptureFramerate:framerate];
|
|
85
54
|
}
|
|
86
55
|
|
|
87
|
-
|
|
88
|
-
NSNumber *sensitivity = [RCTConvert NSNumber:json];
|
|
89
|
-
[view setAutoSensitivity:sensitivity];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
@end
|
|
56
|
+
@end
|