@byteplus/react-native-live-push 1.1.3-rc.0 → 1.1.3-rc.2

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 (123) hide show
  1. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushModule.java +13 -2
  2. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushPackage.java +16 -13
  3. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushView.java +16 -0
  4. package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushViewManager.java +7 -2
  5. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +392 -0
  6. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +415 -0
  7. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerViewManager.java +79 -0
  8. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/TextureMgr.java +168 -0
  9. package/android/src/main/java/com/volcengine/velive/rn/push/mixer/YuvHelper.java +154 -0
  10. package/ios/VeLiveMixerHelper.h +44 -0
  11. package/ios/VeLiveMixerHelper.m +562 -0
  12. package/ios/VeLiveMixerView.h +69 -0
  13. package/ios/VeLiveMixerView.m +592 -0
  14. package/ios/VeLiveMixerViewManager.m +92 -0
  15. package/lib/commonjs/index.js +1779 -980
  16. package/lib/commonjs/typescript/android/index.d.ts +47 -0
  17. package/lib/commonjs/typescript/codegen/android/api.d.ts +1527 -0
  18. package/lib/commonjs/typescript/codegen/android/callback.d.ts +91 -0
  19. package/lib/commonjs/typescript/codegen/android/errorcode.d.ts +26 -0
  20. package/lib/commonjs/typescript/codegen/android/index.d.ts +5 -0
  21. package/lib/commonjs/typescript/codegen/android/keytype.d.ts +846 -0
  22. package/lib/commonjs/typescript/codegen/android/types.d.ts +33 -0
  23. package/lib/commonjs/typescript/codegen/ios/api.d.ts +222 -0
  24. package/lib/commonjs/typescript/codegen/ios/callback.d.ts +80 -0
  25. package/lib/commonjs/typescript/codegen/ios/errorcode.d.ts +54 -0
  26. package/lib/commonjs/typescript/codegen/ios/external.d.ts +1 -0
  27. package/lib/commonjs/typescript/codegen/ios/index.d.ts +6 -0
  28. package/lib/commonjs/typescript/codegen/ios/keytype.d.ts +460 -0
  29. package/lib/commonjs/typescript/codegen/ios/types.d.ts +46 -0
  30. package/lib/commonjs/typescript/codegen/pack/api.d.ts +1835 -0
  31. package/lib/commonjs/typescript/codegen/pack/callback.d.ts +400 -0
  32. package/lib/commonjs/typescript/codegen/pack/errorcode.d.ts +35 -0
  33. package/lib/commonjs/typescript/codegen/pack/index.d.ts +5 -0
  34. package/lib/commonjs/typescript/codegen/pack/keytype.d.ts +1392 -0
  35. package/lib/commonjs/typescript/codegen/pack/types.d.ts +68 -0
  36. package/lib/commonjs/typescript/codegen/type-shim.d.ts +6 -0
  37. package/lib/commonjs/typescript/component.d.ts +15 -0
  38. package/lib/commonjs/typescript/core/api.d.ts +17 -0
  39. package/lib/commonjs/typescript/core/callback.d.ts +2 -0
  40. package/lib/commonjs/typescript/core/env.d.ts +29 -0
  41. package/lib/commonjs/typescript/core/errorcode.d.ts +2 -0
  42. package/lib/commonjs/typescript/core/index.d.ts +6 -0
  43. package/lib/commonjs/typescript/core/keytype.d.ts +17 -0
  44. package/lib/commonjs/typescript/core/mixer.d.ts +26 -0
  45. package/lib/commonjs/typescript/core/pusher.d.ts +16 -0
  46. package/lib/commonjs/typescript/index.d.ts +3 -0
  47. package/lib/commonjs/typescript/ios/extends.d.ts +41 -0
  48. package/lib/commonjs/typescript/platforms/android/extends.d.ts +8 -0
  49. package/lib/commonjs/typescript/platforms/android/helper.d.ts +8 -0
  50. package/lib/commonjs/typescript/platforms/android/mixer.d.ts +8 -0
  51. package/lib/commonjs/typescript/platforms/ios/extends.d.ts +17 -0
  52. package/lib/commonjs/typescript/platforms/ios/helper.d.ts +8 -0
  53. package/lib/commonjs/typescript/platforms/ios/mixer.d.ts +9 -0
  54. package/lib/commonjs/typescript/runtime.d.ts +1 -0
  55. package/lib/commonjs/typescript/view/MixView.d.ts +52 -0
  56. package/lib/commonjs/typescript/view/VeImageView.d.ts +19 -0
  57. package/lib/commonjs/typescript/view/VeTextView.d.ts +7 -0
  58. package/lib/commonjs/typescript/view/VeView.d.ts +7 -0
  59. package/lib/commonjs/typescript/view/VeWebView.d.ts +7 -0
  60. package/lib/commonjs/typescript/view/index.d.ts +5 -0
  61. package/lib/module/index.js +1776 -982
  62. package/lib/module/typescript/android/index.d.ts +47 -0
  63. package/lib/module/typescript/codegen/android/api.d.ts +1527 -0
  64. package/lib/module/typescript/codegen/android/callback.d.ts +91 -0
  65. package/lib/module/typescript/codegen/android/errorcode.d.ts +26 -0
  66. package/lib/module/typescript/codegen/android/index.d.ts +5 -0
  67. package/lib/module/typescript/codegen/android/keytype.d.ts +846 -0
  68. package/lib/module/typescript/codegen/android/types.d.ts +33 -0
  69. package/lib/module/typescript/codegen/ios/api.d.ts +222 -0
  70. package/lib/module/typescript/codegen/ios/callback.d.ts +80 -0
  71. package/lib/module/typescript/codegen/ios/errorcode.d.ts +54 -0
  72. package/lib/module/typescript/codegen/ios/external.d.ts +1 -0
  73. package/lib/module/typescript/codegen/ios/index.d.ts +6 -0
  74. package/lib/module/typescript/codegen/ios/keytype.d.ts +460 -0
  75. package/lib/module/typescript/codegen/ios/types.d.ts +46 -0
  76. package/lib/module/typescript/codegen/pack/api.d.ts +1835 -0
  77. package/lib/module/typescript/codegen/pack/callback.d.ts +400 -0
  78. package/lib/module/typescript/codegen/pack/errorcode.d.ts +35 -0
  79. package/lib/module/typescript/codegen/pack/index.d.ts +5 -0
  80. package/lib/module/typescript/codegen/pack/keytype.d.ts +1392 -0
  81. package/lib/module/typescript/codegen/pack/types.d.ts +68 -0
  82. package/lib/module/typescript/codegen/type-shim.d.ts +6 -0
  83. package/lib/module/typescript/component.d.ts +15 -0
  84. package/lib/module/typescript/core/api.d.ts +17 -0
  85. package/lib/module/typescript/core/callback.d.ts +2 -0
  86. package/lib/module/typescript/core/env.d.ts +29 -0
  87. package/lib/module/typescript/core/errorcode.d.ts +2 -0
  88. package/lib/module/typescript/core/index.d.ts +6 -0
  89. package/lib/module/typescript/core/keytype.d.ts +17 -0
  90. package/lib/module/typescript/core/mixer.d.ts +26 -0
  91. package/lib/module/typescript/core/pusher.d.ts +16 -0
  92. package/lib/module/typescript/index.d.ts +3 -0
  93. package/lib/module/typescript/ios/extends.d.ts +41 -0
  94. package/lib/module/typescript/platforms/android/extends.d.ts +8 -0
  95. package/lib/module/typescript/platforms/android/helper.d.ts +8 -0
  96. package/lib/module/typescript/platforms/android/mixer.d.ts +8 -0
  97. package/lib/module/typescript/platforms/ios/extends.d.ts +17 -0
  98. package/lib/module/typescript/platforms/ios/helper.d.ts +8 -0
  99. package/lib/module/typescript/platforms/ios/mixer.d.ts +9 -0
  100. package/lib/module/typescript/runtime.d.ts +1 -0
  101. package/lib/module/typescript/view/MixView.d.ts +52 -0
  102. package/lib/module/typescript/view/VeImageView.d.ts +19 -0
  103. package/lib/module/typescript/view/VeTextView.d.ts +7 -0
  104. package/lib/module/typescript/view/VeView.d.ts +7 -0
  105. package/lib/module/typescript/view/VeWebView.d.ts +7 -0
  106. package/lib/module/typescript/view/index.d.ts +5 -0
  107. package/lib/typescript/codegen/android/api.d.ts +1 -121
  108. package/lib/typescript/codegen/ios/api.d.ts +1 -28
  109. package/lib/typescript/codegen/pack/api.d.ts +1 -133
  110. package/lib/typescript/core/api.d.ts +17 -2
  111. package/lib/typescript/core/keytype.d.ts +15 -0
  112. package/lib/typescript/core/mixer.d.ts +26 -0
  113. package/lib/typescript/index.d.ts +1 -0
  114. package/lib/typescript/platforms/android/extends.d.ts +8 -0
  115. package/lib/typescript/platforms/android/mixer.d.ts +8 -0
  116. package/lib/typescript/platforms/ios/mixer.d.ts +9 -0
  117. package/lib/typescript/view/MixView.d.ts +52 -0
  118. package/lib/typescript/view/VeImageView.d.ts +19 -0
  119. package/lib/typescript/view/VeTextView.d.ts +7 -0
  120. package/lib/typescript/view/VeView.d.ts +7 -0
  121. package/lib/typescript/view/VeWebView.d.ts +7 -0
  122. package/lib/typescript/view/index.d.ts +5 -0
  123. package/package.json +1 -1
@@ -0,0 +1,562 @@
1
+ //
2
+ // VeLiveMixerHelper.m
3
+ // react-native-live-push
4
+ //
5
+ // Created by ByteDance on 2024/12/09.
6
+ //
7
+
8
+ #import "VeLiveMixerHelper.h"
9
+ #include <objc/NSObjCRuntime.h>
10
+ #import <UIKit/UIKit.h>
11
+ #import <TTSDKLivePushRTS/TTSDKLivePushRTS.h>
12
+ #import <mach/mach.h>
13
+ #import <mach/task_info.h>
14
+
15
+ static NSString *TAG = @"VeLiveMixerHelper";
16
+
17
+ #pragma mark - Forward Declaration
18
+
19
+ @class VeLiveMixerBitmapCallback;
20
+
21
+ #pragma mark - VeLiveMixerHelper Implementation
22
+
23
+ @interface VeLiveMixerHelper ()
24
+
25
+ @property(nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary *> *mixedViewLayout;
26
+ @property(nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *viewIdToStreamId;
27
+ @property(nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *pendingCallbacks;
28
+ @property(nonatomic, strong) NSMutableDictionary<NSString *, VeLiveMixerBitmapCallback *> *callbacks;
29
+ @property (nonatomic, strong) NSTimer *memoryMonitorTimer;
30
+
31
+ @end
32
+
33
+ #pragma mark - VeLiveMixerBitmapCallback Declaration
34
+
35
+ @interface VeLiveMixerBitmapCallback : NSObject <VeLiveBitmapCaptureCallback>
36
+ @property (nonatomic, assign) int streamId;
37
+ - (instancetype)initWithStreamId:(int)streamId;
38
+ @end
39
+
40
+ @implementation VeLiveMixerHelper
41
+
42
+ // 对齐Android的static cachedMixedViews
43
+ static NSMutableDictionary<NSString *, VeLiveMixerUIView *> *_cachedMixedViews = nil;
44
+ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释放
45
+
46
+ + (NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
47
+ if (!_cachedMixedViews) {
48
+ _cachedMixedViews = [[NSMutableDictionary alloc] init];
49
+ }
50
+ return _cachedMixedViews;
51
+ }
52
+
53
+ + (void)setCachedMixedViews:(NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
54
+ _cachedMixedViews = cachedMixedViews;
55
+ }
56
+
57
+ - (instancetype)init {
58
+ self = [super init];
59
+ if (self) {
60
+ [self commonInit];
61
+ }
62
+ return self;
63
+ }
64
+
65
+ - (instancetype)initWithPusher:(VeLivePusher *)pusher {
66
+ self = [super init];
67
+ if (self) {
68
+ _pusher = pusher;
69
+ [self commonInit];
70
+ [self startMemoryMonitoring];
71
+ }
72
+ return self;
73
+ }
74
+
75
+ - (void)commonInit {
76
+ _mixedViewLayout = [[NSMutableDictionary alloc] init];
77
+ _viewIdToStreamId = [[NSMutableDictionary alloc] init];
78
+ _pendingCallbacks = [[NSMutableDictionary alloc] init];
79
+ _callbacks = [[NSMutableDictionary alloc] init];
80
+
81
+ currentInstance = self;
82
+ }
83
+
84
+ #pragma mark - Lazy Loading Properties
85
+
86
+ - (NSMutableDictionary *)mixedViewLayout {
87
+ if (!_mixedViewLayout) {
88
+ _mixedViewLayout = [[NSMutableDictionary alloc] init];
89
+ }
90
+ return _mixedViewLayout;
91
+ }
92
+
93
+ - (NSMutableDictionary *)viewIdToStreamId {
94
+ if (!_viewIdToStreamId) {
95
+ _viewIdToStreamId = [[NSMutableDictionary alloc] init];
96
+ }
97
+ return _viewIdToStreamId;
98
+ }
99
+
100
+ - (NSMutableDictionary *)pendingCallbacks {
101
+ if (!_pendingCallbacks) {
102
+ _pendingCallbacks = [[NSMutableDictionary alloc] init];
103
+ }
104
+ return _pendingCallbacks;
105
+ }
106
+
107
+ - (NSMutableDictionary *)callbacks {
108
+ if (!_callbacks) {
109
+ _callbacks = [[NSMutableDictionary alloc] init];
110
+ }
111
+ return _callbacks;
112
+ }
113
+
114
+ #pragma mark - Public Methods
115
+
116
+ - (void)setPusher:(VeLivePusher *)pusher {
117
+ _pusher = pusher;
118
+
119
+ if (currentInstance != self) {
120
+ currentInstance = self;
121
+ }
122
+ }
123
+
124
+ - (int)addView:(NSString *)viewId config:(NSDictionary *)viewInfo {
125
+ @try {
126
+ if (!self.pusher) {
127
+ return -1;
128
+ }
129
+
130
+ VeLiveMixerUIView *view = [VeLiveMixerHelper cachedMixedViews][viewId];
131
+ if (![view isKindOfClass:[VeLiveMixerUIView class]]) {
132
+ // View not ready, store pending callback
133
+ }
134
+
135
+ NSNumber *existingStreamId = [self.viewIdToStreamId objectForKey:viewId];
136
+ if(existingStreamId != nil) {
137
+ return [existingStreamId intValue];
138
+ }
139
+
140
+ int videoStreamId = [[self.pusher getMixerManager] addVideoStream];
141
+
142
+ NSDictionary *layout = @{
143
+ @"x": viewInfo[@"x"] ?: @(0),
144
+ @"y": viewInfo[@"y"] ?: @(0),
145
+ @"width": viewInfo[@"width"] ?: @(0),
146
+ @"height": viewInfo[@"height"] ?: @(0),
147
+ @"zOrder": viewInfo[@"zOrder"] ?: @(1),
148
+ @"renderMode": viewInfo[@"renderMode"] ?: @(0),
149
+ @"streamId": @(videoStreamId)
150
+ };
151
+
152
+ VeLiveStreamMixDescription *description = [[VeLiveStreamMixDescription alloc] init];
153
+ VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
154
+ videoLayout.streamId = videoStreamId;
155
+ videoLayout.x = [viewInfo[@"x"] floatValue];
156
+ videoLayout.y = [viewInfo[@"y"] floatValue];
157
+ videoLayout.width = [viewInfo[@"width"] floatValue];
158
+ videoLayout.height = [viewInfo[@"height"] floatValue];
159
+ videoLayout.zOrder = [viewInfo[@"zOrder"] intValue];
160
+ videoLayout.renderMode = [viewInfo[@"renderMode"] intValue];
161
+ description.mixVideoStreams = @[videoLayout];
162
+ [self.pusher.getMixerManager updateStreamMixDescription:description];
163
+
164
+ [self.mixedViewLayout setObject:layout forKey:viewId];
165
+ [self.viewIdToStreamId setObject:@(videoStreamId) forKey:viewId];
166
+
167
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
168
+ [VeLiveMixerHelper setupCallbackForMixerView:view streamId:videoStreamId];
169
+ } else {
170
+ [self.pendingCallbacks setObject:@(videoStreamId) forKey:viewId];
171
+ }
172
+
173
+ return videoStreamId;
174
+ } @catch (NSException *exception) {
175
+ return -1;
176
+ }
177
+ }
178
+
179
+ - (BOOL)updateView:(NSString *)viewId config:(NSDictionary *)viewInfo {
180
+ @try {
181
+ NSDictionary *layout = [self.mixedViewLayout objectForKey:viewId];
182
+ if (!layout) {
183
+ return NO;
184
+ }
185
+
186
+ NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
187
+ int videoStreamId = [streamIdNumber intValue];
188
+
189
+ NSMutableDictionary *updatedLayout = [layout mutableCopy];
190
+ [updatedLayout setObject:(viewInfo[@"x"] ?: layout[@"x"]) forKey:@"x"];
191
+ [updatedLayout setObject:(viewInfo[@"y"] ?: layout[@"y"]) forKey:@"y"];
192
+ [updatedLayout setObject:(viewInfo[@"width"] ?: layout[@"width"]) forKey:@"width"];
193
+ [updatedLayout setObject:(viewInfo[@"height"] ?: layout[@"height"]) forKey:@"height"];
194
+ [updatedLayout setObject:(viewInfo[@"zOrder"] ?: layout[@"zOrder"]) forKey:@"zOrder"];
195
+ [updatedLayout setObject:(viewInfo[@"renderMode"] ?: layout[@"renderMode"]) forKey:@"renderMode"];
196
+
197
+ VeLiveStreamMixDescription *description = [[VeLiveStreamMixDescription alloc] init];
198
+ VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
199
+ videoLayout.streamId = videoStreamId;
200
+ videoLayout.x = [updatedLayout[@"x"] floatValue];
201
+ videoLayout.y = [updatedLayout[@"y"] floatValue];
202
+ videoLayout.width = [updatedLayout[@"width"] floatValue];
203
+ videoLayout.height = [updatedLayout[@"height"] floatValue];
204
+ videoLayout.zOrder = [updatedLayout[@"zOrder"] intValue];
205
+ videoLayout.renderMode = [updatedLayout[@"renderMode"] intValue];
206
+ description.mixVideoStreams = @[videoLayout];
207
+ [self.pusher.getMixerManager updateStreamMixDescription:description];
208
+
209
+ [self.mixedViewLayout setObject:updatedLayout forKey:viewId];
210
+
211
+ VeLiveMixerUIView *mixerView = [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
212
+ [mixerView performCaptureWithTrigger:@"update"];
213
+
214
+ return YES;
215
+ } @catch (NSException *exception) {
216
+ return NO;
217
+ }
218
+ }
219
+
220
+ - (BOOL)removeView:(NSString *)viewId {
221
+ @try {
222
+ NSDictionary *layout = [self.mixedViewLayout objectForKey:viewId];
223
+ if (!layout) {
224
+ return NO;
225
+ }
226
+
227
+ NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
228
+ if (streamIdNumber) {
229
+ int streamId = [streamIdNumber intValue];
230
+ [self.pusher.getMixerManager removeVideoStream:streamId];
231
+
232
+ NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
233
+ [self.callbacks removeObjectForKey:callbackKey];
234
+ }
235
+
236
+ [self.viewIdToStreamId removeObjectForKey:viewId];
237
+ [self.pendingCallbacks removeObjectForKey:viewId];
238
+
239
+ VeLiveMixerUIView *view = [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
240
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
241
+ view.bitmapCaptureCallback = nil;
242
+ }
243
+
244
+ [self.mixedViewLayout removeObjectForKey:viewId];
245
+
246
+ return YES;
247
+ } @catch (NSException *exception) {
248
+ return NO;
249
+ }
250
+ }
251
+
252
+ - (void)captureView:(NSString *)viewId {
253
+ VeLiveMixerUIView *view = [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
254
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
255
+ [view performCaptureWithTrigger:@"manual"];
256
+ }
257
+ }
258
+
259
+ #pragma mark - Static Methods
260
+
261
+ + (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView streamId:(int)streamId {
262
+ if (mixerView == nil) {
263
+ return;
264
+ }
265
+
266
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
267
+ if (!instance) {
268
+ return;
269
+ }
270
+
271
+ VeLiveMixerBitmapCallback *callback = [[VeLiveMixerBitmapCallback alloc] initWithStreamId:streamId];
272
+
273
+ NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
274
+ [instance.callbacks setObject:callback forKey:callbackKey];
275
+
276
+ mixerView.bitmapCaptureCallback = callback;
277
+
278
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
279
+ @try {
280
+ if (mixerView && mixerView.bitmapCaptureCallback) {
281
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
282
+ if (instance && instance.pusher) {
283
+ @try {
284
+ NSString *viewId = mixerView.viewId;
285
+ NSDictionary *layout = [instance.mixedViewLayout objectForKey:viewId];
286
+ if (layout) {
287
+ VeLiveStreamMixDescription *description = [[VeLiveStreamMixDescription alloc] init];
288
+ VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
289
+ videoLayout.streamId = streamId;
290
+ videoLayout.x = [layout[@"x"] floatValue];
291
+ videoLayout.y = [layout[@"y"] floatValue];
292
+ videoLayout.width = [layout[@"width"] floatValue];
293
+ videoLayout.height = [layout[@"height"] floatValue];
294
+ videoLayout.zOrder = [layout[@"zOrder"] intValue];
295
+ videoLayout.renderMode = [layout[@"renderMode"] intValue];
296
+ description.mixVideoStreams = @[videoLayout];
297
+ [instance.pusher.getMixerManager updateStreamMixDescription:description];
298
+ }
299
+ } @catch (NSException *e) {
300
+ // Ignore
301
+ }
302
+ }
303
+
304
+ [mixerView performCaptureWithTrigger:@"update"];
305
+ }
306
+ } @catch (NSException *exception) {
307
+ // Ignore
308
+ }
309
+ });
310
+ }
311
+
312
+ + (void)sendBitmapToMixerStatic:(int)streamId bitmap:(UIImage *)bitmap {
313
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
314
+ if (instance && instance.pusher) {
315
+ [instance sendBitmapToMixer:streamId bitmap:bitmap];
316
+ }
317
+ }
318
+
319
+ + (VeLiveMixerHelper *)findActiveMixerManager {
320
+ return currentInstance;
321
+ }
322
+
323
+ + (void)onViewReady:(NSString *)viewId mixerView:(VeLiveMixerUIView *)mixerView {
324
+ @try {
325
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
326
+ if (instance != nil) {
327
+ NSNumber *streamIdNumber = [instance.pendingCallbacks objectForKey:viewId];
328
+ if (streamIdNumber != nil) {
329
+ int streamId = [streamIdNumber intValue];
330
+ [instance.pendingCallbacks removeObjectForKey:viewId];
331
+ [VeLiveMixerHelper setupCallbackForMixerView:mixerView streamId:streamId];
332
+ }
333
+ }
334
+ } @catch (NSException *exception) {
335
+ // Ignore
336
+ }
337
+ }
338
+
339
+ - (void)cleanup {
340
+ [self stopMemoryMonitoring];
341
+
342
+ for (NSString *viewId in self.mixedViewLayout.allKeys) {
343
+ VeLiveMixerUIView *view = [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
344
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
345
+ view.bitmapCaptureCallback = nil;
346
+ }
347
+ }
348
+
349
+ [self.mixedViewLayout removeAllObjects];
350
+ [self.pendingCallbacks removeAllObjects];
351
+ [self.callbacks removeAllObjects];
352
+
353
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
354
+ @autoreleasepool {
355
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
356
+ [[NSURLCache sharedURLCache] setMemoryCapacity:0];
357
+ [[NSURLCache sharedURLCache] setMemoryCapacity:10 * 1024 * 1024];
358
+
359
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:nil];
360
+
361
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
362
+ // Final cleanup completed
363
+ });
364
+ }
365
+ });
366
+ }
367
+
368
+ - (void)dealloc {
369
+ [self cleanup];
370
+
371
+ if (currentInstance == self) {
372
+ currentInstance = nil;
373
+ }
374
+ }
375
+
376
+ #pragma mark - Memory Monitoring
377
+
378
+ - (void)startMemoryMonitoring {
379
+ self.memoryMonitorTimer = [NSTimer scheduledTimerWithTimeInterval:30.0
380
+ target:self
381
+ selector:@selector(checkMemoryUsage)
382
+ userInfo:nil
383
+ repeats:YES];
384
+ }
385
+
386
+ - (void)stopMemoryMonitoring {
387
+ if (self.memoryMonitorTimer) {
388
+ [self.memoryMonitorTimer invalidate];
389
+ self.memoryMonitorTimer = nil;
390
+ }
391
+ }
392
+
393
+ - (void)checkMemoryUsage {
394
+ @autoreleasepool {
395
+ struct task_basic_info info;
396
+ mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
397
+ kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
398
+
399
+ if (kerr == KERN_SUCCESS) {
400
+ double memoryUsageMB = info.resident_size / 1024.0 / 1024.0;
401
+
402
+ if (memoryUsageMB > 200.0) {
403
+ [self performMemoryCleanup];
404
+ }
405
+ }
406
+ }
407
+ }
408
+
409
+ - (void)performMemoryCleanup {
410
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
411
+ @autoreleasepool {
412
+ NSMutableDictionary *cachedViews = [VeLiveMixerHelper cachedMixedViews];
413
+ for (NSString *viewId in cachedViews.allKeys) {
414
+ VeLiveMixerUIView *view = [cachedViews objectForKey:viewId];
415
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
416
+ if ([view.captureMode isEqualToString:@"realtime"]) {
417
+ [view stopCapture];
418
+
419
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
420
+ if (view && [cachedViews objectForKey:viewId] == view) {
421
+ [view startCapture];
422
+ }
423
+ });
424
+ }
425
+ }
426
+ }
427
+
428
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
429
+ [[NSURLCache sharedURLCache] setMemoryCapacity:5 * 1024 * 1024];
430
+
431
+ [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:nil];
432
+ }
433
+ });
434
+ }
435
+
436
+ #pragma mark - Private Methods
437
+
438
+ - (void)sendBitmapToMixer:(int)streamId bitmap:(UIImage *)bitmap {
439
+ @autoreleasepool {
440
+ if (!self.pusher || !bitmap) {
441
+ return;
442
+ }
443
+
444
+ CVPixelBufferRef pixelBuffer = NULL;
445
+
446
+ @try {
447
+ pixelBuffer = [self pixelBufferFromUIImage:bitmap];
448
+ if (!pixelBuffer) {
449
+ return;
450
+ }
451
+
452
+ VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init];
453
+ videoFrame.bufferType = VeLiveVideoBufferTypePixelBuffer;
454
+ videoFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000);
455
+ videoFrame.pixelBuffer = pixelBuffer;
456
+
457
+ __block CVPixelBufferRef bufferToRelease = pixelBuffer;
458
+ [videoFrame setReleaseCallback:^{
459
+ @autoreleasepool {
460
+ if (bufferToRelease) {
461
+ CVPixelBufferRelease(bufferToRelease);
462
+ bufferToRelease = NULL;
463
+ }
464
+ }
465
+ }];
466
+
467
+ [[self.pusher getMixerManager] sendCustomVideoFrame:videoFrame streamId:streamId];
468
+
469
+ pixelBuffer = NULL;
470
+
471
+ } @catch (NSException *exception) {
472
+ if (pixelBuffer) {
473
+ CVPixelBufferRelease(pixelBuffer);
474
+ pixelBuffer = NULL;
475
+ }
476
+ }
477
+ }
478
+ }
479
+
480
+ #pragma mark - Helper Methods
481
+
482
+ - (CVPixelBufferRef)pixelBufferFromUIImage:(UIImage *)image {
483
+ if (!image) {
484
+ return NULL;
485
+ }
486
+
487
+ CGImageRef cgImage = image.CGImage;
488
+ if (!cgImage) {
489
+ return NULL;
490
+ }
491
+
492
+ CGFloat width = CGImageGetWidth(cgImage);
493
+ CGFloat height = CGImageGetHeight(cgImage);
494
+
495
+ NSDictionary *options = @{
496
+ (NSString *)kCVPixelBufferCGImageCompatibilityKey: @YES,
497
+ (NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES,
498
+ (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}
499
+ };
500
+
501
+ CVPixelBufferRef pixelBuffer = NULL;
502
+ CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
503
+ width, height,
504
+ kCVPixelFormatType_32BGRA,
505
+ (__bridge CFDictionaryRef)options,
506
+ &pixelBuffer);
507
+
508
+ if (status != kCVReturnSuccess || !pixelBuffer) {
509
+ return NULL;
510
+ }
511
+
512
+ CVPixelBufferLockBaseAddress(pixelBuffer, 0);
513
+
514
+ void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);
515
+ size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
516
+
517
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
518
+ CGContextRef context = CGBitmapContextCreate(pixelData, width, height,
519
+ 8, bytesPerRow, colorSpace,
520
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
521
+
522
+ if (!context) {
523
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
524
+ CVPixelBufferRelease(pixelBuffer);
525
+ CGColorSpaceRelease(colorSpace);
526
+ return NULL;
527
+ }
528
+
529
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
530
+
531
+ CGContextRelease(context);
532
+ CGColorSpaceRelease(colorSpace);
533
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
534
+
535
+ return pixelBuffer;
536
+ }
537
+
538
+ @end
539
+
540
+ #pragma mark - VeLiveMixerBitmapCallback Implementation
541
+
542
+ @implementation VeLiveMixerBitmapCallback
543
+
544
+ - (instancetype)initWithStreamId:(int)streamId {
545
+ self = [super init];
546
+ if (self) {
547
+ _streamId = streamId;
548
+ }
549
+ return self;
550
+ }
551
+
552
+ - (void)onBitmapCaptured:(UIImage *)bitmap {
553
+ if (bitmap != nil) {
554
+ [VeLiveMixerHelper sendBitmapToMixerStatic:self.streamId bitmap:bitmap];
555
+ }
556
+ }
557
+
558
+ - (void)onCaptureError:(NSString *)error {
559
+ // Ignore
560
+ }
561
+
562
+ @end
@@ -0,0 +1,69 @@
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) NSNumber *x;
35
+ @property (nonatomic, strong) NSNumber *y;
36
+ @property (nonatomic, strong) NSNumber *width;
37
+ @property (nonatomic, strong) NSNumber *height;
38
+ @property (nonatomic, strong) NSNumber *zOrder;
39
+ @property (nonatomic, strong) NSNumber *renderMode;
40
+ @property (nonatomic, strong) NSString *captureMode;
41
+ @property (nonatomic, strong) NSNumber *captureFramerate;
42
+ @property (nonatomic, strong) NSNumber *autoSensitivity;
43
+
44
+ // React Native 事件回调
45
+ @property (nonatomic, copy) RCTBubblingEventBlock onBitmapCapture;
46
+ @property (nonatomic, copy) RCTBubblingEventBlock onCaptureError;
47
+
48
+ // 代理
49
+ @property (nonatomic, weak) id<VeLiveMixerViewDelegate> delegate;
50
+
51
+ // Bitmap捕获回调 - 用于与MixerManager集成
52
+ @property (nonatomic, weak) id<VeLiveBitmapCaptureCallback> bitmapCaptureCallback;
53
+
54
+ // 控制方法
55
+ - (void)startCapture;
56
+ - (void)stopCapture;
57
+ - (void)performCaptureWithTrigger:(NSString *)trigger;
58
+
59
+ // 设置bitmap捕获回调
60
+ - (void)setBitmapCaptureCallback:(id<VeLiveBitmapCaptureCallback>)callback;
61
+
62
+ // UIView转换方法
63
+ - (UIImage *)snapshotIncludeSubviews;
64
+ - (CVPixelBufferRef)createPixelBuffer;
65
+ - (CMSampleBufferRef)createSampleBuffer;
66
+
67
+ @end
68
+
69
+ NS_ASSUME_NONNULL_END