@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,49 @@
1
+ //
2
+ // VeLiveMixerHelper.h
3
+ // react-native-live-push
4
+ //
5
+ // Created by ByteDance on 2024/12/09.
6
+ //
7
+
8
+ #import "VeLiveMixerView.h"
9
+ #import <CoreVideo/CoreVideo.h>
10
+ #import <Foundation/Foundation.h>
11
+
12
+ NS_ASSUME_NONNULL_BEGIN
13
+
14
+ // Forward declaration
15
+ @class VeLivePusher;
16
+
17
+ // Mixer helper class - fully aligned with Android MixerManager.java
18
+ @interface VeLiveMixerHelper : NSObject <VeLiveBitmapCaptureCallback>
19
+
20
+ // Aligned with Android's static cachedMixedViews
21
+ @property(class, nonatomic, strong)
22
+ NSMutableDictionary<NSString *, VeLiveMixerUIView *> *cachedMixedViews;
23
+
24
+ @property(nonatomic, weak) VeLivePusher *pusher;
25
+
26
+ - (instancetype)initWithPusher:(VeLivePusher *)pusher;
27
+
28
+ // Public methods - fully aligned with Android MixerManager's public interface
29
+ - (void)setPusher:(VeLivePusher *)pusher;
30
+ - (int)addView:(NSString *)viewId config:(NSDictionary *)viewInfo;
31
+ - (BOOL)updateView:(NSString *)viewId config:(NSDictionary *)viewInfo;
32
+ - (BOOL)removeView:(NSString *)viewId;
33
+ - (void)captureView:(NSString *)viewId;
34
+
35
+ // Static methods - for sending captured content to mixer
36
+ + (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView
37
+ streamId:(int)streamId;
38
+ + (void)sendBitmapToMixerStatic:(int)streamId bitmap:(UIImage *)bitmap;
39
+ + (void)sendPixelBufferToMixerStatic:(int)streamId
40
+ pixelBuffer:(CVPixelBufferRef)pixelBuffer;
41
+ + (VeLiveMixerHelper *)findActiveMixerManager;
42
+ + (void)onViewReady:(NSString *)viewId mixerView:(VeLiveMixerUIView *)mixerView;
43
+
44
+ // Resource cleanup
45
+ - (void)cleanup;
46
+
47
+ @end
48
+
49
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,646 @@
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
+ #import <TTSDKFramework/VeLivePusher.h>
10
+ #import <UIKit/UIKit.h>
11
+ #import <mach/mach.h>
12
+ #import <mach/task_info.h>
13
+ #include <objc/NSObjCRuntime.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)
26
+ NSMutableDictionary<NSString *, NSDictionary *> *mixedViewLayout;
27
+ @property(nonatomic, strong)
28
+ NSMutableDictionary<NSString *, NSNumber *> *viewIdToStreamId;
29
+ @property(nonatomic, strong)
30
+ NSMutableDictionary<NSString *, NSNumber *> *pendingCallbacks;
31
+ @property(nonatomic, strong)
32
+ NSMutableDictionary<NSString *, VeLiveMixerBitmapCallback *> *callbacks;
33
+ @property(nonatomic, strong) NSTimer *memoryMonitorTimer;
34
+
35
+ @end
36
+
37
+ #pragma mark - VeLiveMixerBitmapCallback Declaration
38
+
39
+ @interface VeLiveMixerBitmapCallback : NSObject <VeLiveBitmapCaptureCallback>
40
+ @property(nonatomic, assign) int streamId;
41
+ - (instancetype)initWithStreamId:(int)streamId;
42
+ @end
43
+
44
+ @implementation VeLiveMixerHelper
45
+
46
+ // Aligned with Android's static cachedMixedViews
47
+ static NSMutableDictionary<NSString *, VeLiveMixerUIView *> *_cachedMixedViews =
48
+ nil;
49
+ static VeLiveMixerHelper *currentInstance =
50
+ nil; // Strong reference to prevent ARC deallocation
51
+
52
+ + (NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
53
+ if (!_cachedMixedViews) {
54
+ _cachedMixedViews = [[NSMutableDictionary alloc] init];
55
+ }
56
+ return _cachedMixedViews;
57
+ }
58
+
59
+ + (void)setCachedMixedViews:
60
+ (NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
61
+ _cachedMixedViews = cachedMixedViews;
62
+ }
63
+
64
+ - (instancetype)init {
65
+ self = [super init];
66
+ if (self) {
67
+ [self commonInit];
68
+ }
69
+ return self;
70
+ }
71
+
72
+ - (instancetype)initWithPusher:(VeLivePusher *)pusher {
73
+ self = [super init];
74
+ if (self) {
75
+ _pusher = pusher;
76
+ [self commonInit];
77
+ [self startMemoryMonitoring];
78
+ }
79
+ return self;
80
+ }
81
+
82
+ - (void)commonInit {
83
+ _mixedViewLayout = [[NSMutableDictionary alloc] init];
84
+ _viewIdToStreamId = [[NSMutableDictionary alloc] init];
85
+ _pendingCallbacks = [[NSMutableDictionary alloc] init];
86
+ _callbacks = [[NSMutableDictionary alloc] init];
87
+
88
+ currentInstance = self;
89
+ }
90
+
91
+ #pragma mark - Lazy Loading Properties
92
+
93
+ - (NSMutableDictionary *)mixedViewLayout {
94
+ if (!_mixedViewLayout) {
95
+ _mixedViewLayout = [[NSMutableDictionary alloc] init];
96
+ }
97
+ return _mixedViewLayout;
98
+ }
99
+
100
+ - (NSMutableDictionary *)viewIdToStreamId {
101
+ if (!_viewIdToStreamId) {
102
+ _viewIdToStreamId = [[NSMutableDictionary alloc] init];
103
+ }
104
+ return _viewIdToStreamId;
105
+ }
106
+
107
+ - (NSMutableDictionary *)pendingCallbacks {
108
+ if (!_pendingCallbacks) {
109
+ _pendingCallbacks = [[NSMutableDictionary alloc] init];
110
+ }
111
+ return _pendingCallbacks;
112
+ }
113
+
114
+ - (NSMutableDictionary *)callbacks {
115
+ if (!_callbacks) {
116
+ _callbacks = [[NSMutableDictionary alloc] init];
117
+ }
118
+ return _callbacks;
119
+ }
120
+
121
+ #pragma mark - Public Methods
122
+
123
+ - (void)setPusher:(VeLivePusher *)pusher {
124
+ _pusher = pusher;
125
+
126
+ if (currentInstance != self) {
127
+ currentInstance = self;
128
+ }
129
+ }
130
+
131
+ - (int)addView:(NSString *)viewId config:(NSDictionary *)viewInfo {
132
+ @try {
133
+ if (!self.pusher) {
134
+ return -1;
135
+ }
136
+
137
+ VeLiveMixerUIView *view = [VeLiveMixerHelper cachedMixedViews][viewId];
138
+ if (![view isKindOfClass:[VeLiveMixerUIView class]]) {
139
+ // View not ready, store pending callback
140
+ }
141
+
142
+ NSNumber *existingStreamId = [self.viewIdToStreamId objectForKey:viewId];
143
+ if (existingStreamId != nil) {
144
+ return [existingStreamId intValue];
145
+ }
146
+
147
+ int videoStreamId = [[self.pusher getMixerManager] addVideoStream];
148
+
149
+ NSDictionary *layout = @{
150
+ @"x" : viewInfo[@"x"] ?: @(0),
151
+ @"y" : viewInfo[@"y"] ?: @(0),
152
+ @"width" : viewInfo[@"width"] ?: @(0),
153
+ @"height" : viewInfo[@"height"] ?: @(0),
154
+ @"alpha" : viewInfo[@"alpha"] ?: @(1.0),
155
+ @"zOrder" : viewInfo[@"zOrder"] ?: @(1),
156
+ @"renderMode" : viewInfo[@"renderMode"] ?: @(0),
157
+ @"streamId" : @(videoStreamId)
158
+ };
159
+
160
+ VeLiveStreamMixDescription *description =
161
+ [[VeLiveStreamMixDescription alloc] init];
162
+ VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
163
+ videoLayout.streamId = videoStreamId;
164
+ videoLayout.x = [viewInfo[@"x"] floatValue];
165
+ videoLayout.y = [viewInfo[@"y"] floatValue];
166
+ videoLayout.width = [viewInfo[@"width"] floatValue];
167
+ videoLayout.height = [viewInfo[@"height"] floatValue];
168
+ videoLayout.zOrder = [viewInfo[@"zOrder"] intValue];
169
+ videoLayout.alpha = [viewInfo[@"alpha"] floatValue] ?: 1.0;
170
+ videoLayout.renderMode = [viewInfo[@"renderMode"] intValue];
171
+ description.mixVideoStreams = @[ videoLayout ];
172
+ [self.pusher.getMixerManager updateStreamMixDescription:description];
173
+
174
+ [self.mixedViewLayout setObject:layout forKey:viewId];
175
+ [self.viewIdToStreamId setObject:@(videoStreamId) forKey:viewId];
176
+
177
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
178
+ [VeLiveMixerHelper setupCallbackForMixerView:view streamId:videoStreamId];
179
+
180
+ // Delayed capture execution to ensure view layout completion
181
+ dispatch_after(
182
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)),
183
+ dispatch_get_main_queue(), ^{
184
+ @try {
185
+ if (view && view.bitmapCaptureCallback) {
186
+ [view performCaptureWithTrigger:@"addView"];
187
+ }
188
+ } @catch (NSException *exception) {
189
+ // Ignore
190
+ }
191
+ });
192
+ } else {
193
+ [self.pendingCallbacks setObject:@(videoStreamId) forKey:viewId];
194
+ }
195
+
196
+ return videoStreamId;
197
+ } @catch (NSException *exception) {
198
+ return -1;
199
+ }
200
+ }
201
+
202
+ - (BOOL)updateView:(NSString *)viewId config:(NSDictionary *)viewInfo {
203
+ @try {
204
+ NSDictionary *layout = [self.mixedViewLayout objectForKey:viewId];
205
+ if (!layout) {
206
+ return NO;
207
+ }
208
+
209
+ NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
210
+ int videoStreamId = [streamIdNumber intValue];
211
+
212
+ NSMutableDictionary *updatedLayout = [layout mutableCopy];
213
+ [updatedLayout setObject:(viewInfo[@"x"] ?: layout[@"x"]) forKey:@"x"];
214
+ [updatedLayout setObject:(viewInfo[@"y"] ?: layout[@"y"]) forKey:@"y"];
215
+ [updatedLayout setObject:(viewInfo[@"width"] ?: layout[@"width"])
216
+ forKey:@"width"];
217
+ [updatedLayout setObject:(viewInfo[@"height"] ?: layout[@"height"])
218
+ forKey:@"height"];
219
+ [updatedLayout setObject:(viewInfo[@"zOrder"] ?: layout[@"zOrder"])
220
+ forKey:@"zOrder"];
221
+ [updatedLayout setObject:(viewInfo[@"alpha"] ?: layout[@"alpha"])
222
+ forKey:@"alpha"];
223
+ [updatedLayout setObject:(viewInfo[@"renderMode"] ?: layout[@"renderMode"])
224
+ forKey:@"renderMode"];
225
+
226
+ VeLiveStreamMixDescription *description =
227
+ [[VeLiveStreamMixDescription alloc] init];
228
+ VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
229
+ videoLayout.streamId = videoStreamId;
230
+ videoLayout.x = [updatedLayout[@"x"] floatValue];
231
+ videoLayout.y = [updatedLayout[@"y"] floatValue];
232
+ videoLayout.width = [updatedLayout[@"width"] floatValue];
233
+ videoLayout.height = [updatedLayout[@"height"] floatValue];
234
+ videoLayout.alpha = [updatedLayout[@"alpha"] floatValue];
235
+ videoLayout.zOrder = [updatedLayout[@"zOrder"] intValue];
236
+ videoLayout.renderMode = [updatedLayout[@"renderMode"] intValue];
237
+ description.mixVideoStreams = @[ videoLayout ];
238
+ [self.pusher.getMixerManager updateStreamMixDescription:description];
239
+
240
+ [self.mixedViewLayout setObject:updatedLayout forKey:viewId];
241
+
242
+ // Delayed capture execution to ensure property updates completion
243
+ VeLiveMixerUIView *mixerView =
244
+ [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
245
+ if ([mixerView isKindOfClass:[VeLiveMixerUIView class]]) {
246
+ dispatch_after(
247
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)),
248
+ dispatch_get_main_queue(), ^{
249
+ @try {
250
+ [mixerView performCaptureWithTrigger:@"update"];
251
+ } @catch (NSException *exception) {
252
+ // Ignore
253
+ }
254
+ });
255
+ }
256
+
257
+ return YES;
258
+ } @catch (NSException *exception) {
259
+ return NO;
260
+ }
261
+ }
262
+
263
+ - (BOOL)removeView:(NSString *)viewId {
264
+ @try {
265
+ NSDictionary *layout = [self.mixedViewLayout objectForKey:viewId];
266
+ if (!layout) {
267
+ return NO;
268
+ }
269
+
270
+ NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
271
+ if (streamIdNumber) {
272
+ int streamId = [streamIdNumber intValue];
273
+ [self.pusher.getMixerManager removeVideoStream:streamId];
274
+
275
+ NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
276
+ [self.callbacks removeObjectForKey:callbackKey];
277
+ }
278
+
279
+ [self.viewIdToStreamId removeObjectForKey:viewId];
280
+ [self.pendingCallbacks removeObjectForKey:viewId];
281
+
282
+ VeLiveMixerUIView *view =
283
+ [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
284
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
285
+ view.bitmapCaptureCallback = nil;
286
+ }
287
+
288
+ [self.mixedViewLayout removeObjectForKey:viewId];
289
+
290
+ return YES;
291
+ } @catch (NSException *exception) {
292
+ return NO;
293
+ }
294
+ }
295
+
296
+ - (void)captureView:(NSString *)viewId {
297
+ VeLiveMixerUIView *view =
298
+ [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
299
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
300
+ [view performCaptureWithTrigger:@"manual"];
301
+ }
302
+ }
303
+
304
+ #pragma mark - Static Methods
305
+
306
+ + (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView
307
+ streamId:(int)streamId {
308
+ if (mixerView == nil) {
309
+ return;
310
+ }
311
+
312
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
313
+ if (!instance) {
314
+ return;
315
+ }
316
+
317
+ VeLiveMixerBitmapCallback *callback =
318
+ [[VeLiveMixerBitmapCallback alloc] initWithStreamId:streamId];
319
+
320
+ NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
321
+ [instance.callbacks setObject:callback forKey:callbackKey];
322
+
323
+ mixerView.bitmapCaptureCallback = callback;
324
+
325
+ dispatch_after(
326
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
327
+ dispatch_get_main_queue(), ^{
328
+ @try {
329
+ if (mixerView && mixerView.bitmapCaptureCallback) {
330
+ VeLiveMixerHelper *instance =
331
+ [VeLiveMixerHelper findActiveMixerManager];
332
+ if (instance && instance.pusher) {
333
+ @try {
334
+ NSString *viewId = mixerView.viewId;
335
+ NSDictionary *layout =
336
+ [instance.mixedViewLayout objectForKey:viewId];
337
+ if (layout) {
338
+ VeLiveStreamMixDescription *description =
339
+ [[VeLiveStreamMixDescription alloc] init];
340
+ VeLiveMixVideoLayout *videoLayout =
341
+ [[VeLiveMixVideoLayout alloc] init];
342
+ videoLayout.streamId = streamId;
343
+ videoLayout.x = [layout[@"x"] floatValue];
344
+ videoLayout.y = [layout[@"y"] floatValue];
345
+ videoLayout.width = [layout[@"width"] floatValue];
346
+ videoLayout.height = [layout[@"height"] floatValue];
347
+ videoLayout.zOrder = [layout[@"zOrder"] intValue];
348
+ videoLayout.alpha = [layout[@"alpha"] floatValue];
349
+ videoLayout.renderMode = [layout[@"renderMode"] intValue];
350
+ description.mixVideoStreams = @[ videoLayout ];
351
+ [instance.pusher.getMixerManager
352
+ updateStreamMixDescription:description];
353
+ }
354
+ } @catch (NSException *e) {
355
+ // Ignore
356
+ }
357
+ }
358
+
359
+ [mixerView performCaptureWithTrigger:@"update"];
360
+ }
361
+ } @catch (NSException *exception) {
362
+ // Ignore
363
+ }
364
+ });
365
+ }
366
+
367
+ + (void)sendBitmapToMixerStatic:(int)streamId bitmap:(UIImage *)bitmap {
368
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
369
+ if (instance && instance.pusher) {
370
+ [instance sendBitmapToMixer:streamId bitmap:bitmap];
371
+ }
372
+ }
373
+
374
+ // New: Send pixelBuffer directly to mixer, avoiding UIImage conversion overhead
375
+ + (void)sendPixelBufferToMixerStatic:(int)streamId
376
+ pixelBuffer:(CVPixelBufferRef)pixelBuffer {
377
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
378
+ if (instance && instance.pusher) {
379
+ [instance sendPixelBufferToMixer:streamId pixelBuffer:pixelBuffer];
380
+ }
381
+ }
382
+
383
+ + (VeLiveMixerHelper *)findActiveMixerManager {
384
+ return currentInstance;
385
+ }
386
+
387
+ + (void)onViewReady:(NSString *)viewId
388
+ mixerView:(VeLiveMixerUIView *)mixerView {
389
+ @try {
390
+ VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
391
+ if (instance != nil) {
392
+ NSNumber *streamIdNumber =
393
+ [instance.pendingCallbacks objectForKey:viewId];
394
+ if (streamIdNumber != nil) {
395
+ int streamId = [streamIdNumber intValue];
396
+ [instance.pendingCallbacks removeObjectForKey:viewId];
397
+ [VeLiveMixerHelper setupCallbackForMixerView:mixerView
398
+ streamId:streamId];
399
+ }
400
+ }
401
+ } @catch (NSException *exception) {
402
+ // Ignore
403
+ }
404
+ }
405
+
406
+ - (void)cleanup {
407
+ [self stopMemoryMonitoring];
408
+
409
+ for (NSString *viewId in self.mixedViewLayout.allKeys) {
410
+ VeLiveMixerUIView *view =
411
+ [[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
412
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
413
+ view.bitmapCaptureCallback = nil;
414
+ }
415
+ }
416
+
417
+ [self.mixedViewLayout removeAllObjects];
418
+ [self.pendingCallbacks removeAllObjects];
419
+ [self.callbacks removeAllObjects];
420
+
421
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
422
+ @autoreleasepool {
423
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
424
+ [[NSURLCache sharedURLCache] setMemoryCapacity:0];
425
+ [[NSURLCache sharedURLCache] setMemoryCapacity:10 * 1024 * 1024];
426
+
427
+ [[NSNotificationCenter defaultCenter]
428
+ postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
429
+ object:nil];
430
+
431
+ dispatch_after(
432
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
433
+ dispatch_get_main_queue(),
434
+ ^{
435
+ // Final cleanup completed
436
+ });
437
+ }
438
+ });
439
+ }
440
+
441
+ - (void)dealloc {
442
+ [self cleanup];
443
+
444
+ if (currentInstance == self) {
445
+ currentInstance = nil;
446
+ }
447
+ }
448
+
449
+ #pragma mark - Memory Monitoring
450
+
451
+ - (void)startMemoryMonitoring {
452
+ self.memoryMonitorTimer =
453
+ [NSTimer scheduledTimerWithTimeInterval:30.0
454
+ target:self
455
+ selector:@selector(checkMemoryUsage)
456
+ userInfo:nil
457
+ repeats:YES];
458
+ }
459
+
460
+ - (void)stopMemoryMonitoring {
461
+ if (self.memoryMonitorTimer) {
462
+ [self.memoryMonitorTimer invalidate];
463
+ self.memoryMonitorTimer = nil;
464
+ }
465
+ }
466
+
467
+ - (void)checkMemoryUsage {
468
+ @autoreleasepool {
469
+ struct task_basic_info info;
470
+ mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
471
+ kern_return_t kerr =
472
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
473
+
474
+ if (kerr == KERN_SUCCESS) {
475
+ double memoryUsageMB = info.resident_size / 1024.0 / 1024.0;
476
+
477
+ if (memoryUsageMB > 200.0) {
478
+ [self performMemoryCleanup];
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ - (void)performMemoryCleanup {
485
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
486
+ @autoreleasepool {
487
+ NSMutableDictionary *cachedViews = [VeLiveMixerHelper cachedMixedViews];
488
+ for (NSString *viewId in cachedViews.allKeys) {
489
+ VeLiveMixerUIView *view = [cachedViews objectForKey:viewId];
490
+ if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
491
+ if ([view.captureMode isEqualToString:@"realtime"]) {
492
+ [view stopCapture];
493
+
494
+ dispatch_after(
495
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
496
+ dispatch_get_main_queue(), ^{
497
+ if (view && [cachedViews objectForKey:viewId] == view) {
498
+ [view startCapture];
499
+ }
500
+ });
501
+ }
502
+ }
503
+ }
504
+
505
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
506
+ [[NSURLCache sharedURLCache] setMemoryCapacity:5 * 1024 * 1024];
507
+
508
+ [[NSNotificationCenter defaultCenter]
509
+ postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
510
+ object:nil];
511
+ }
512
+ });
513
+ }
514
+
515
+ #pragma mark - Private Methods
516
+
517
+ // Send UIImage to mixer (via pixelBuffer conversion)
518
+ - (void)sendBitmapToMixer:(int)streamId bitmap:(UIImage *)bitmap {
519
+ @autoreleasepool {
520
+ if (!self.pusher || !bitmap) {
521
+ return;
522
+ }
523
+
524
+ CVPixelBufferRef pixelBuffer = NULL;
525
+
526
+ @try {
527
+ pixelBuffer = [self pixelBufferFromUIImage:bitmap];
528
+ if (!pixelBuffer) {
529
+ return;
530
+ }
531
+
532
+ // Use new pixelBuffer method
533
+ [self sendPixelBufferToMixer:streamId pixelBuffer:pixelBuffer];
534
+
535
+ } @finally {
536
+ if (pixelBuffer) {
537
+ CVPixelBufferRelease(pixelBuffer);
538
+ pixelBuffer = NULL;
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ // Send pixelBuffer directly to mixer, better performance
545
+ - (void)sendPixelBufferToMixer:(int)streamId
546
+ pixelBuffer:(CVPixelBufferRef)pixelBuffer {
547
+ @autoreleasepool {
548
+ if (!self.pusher || !pixelBuffer) {
549
+ return;
550
+ }
551
+
552
+ @try {
553
+ VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init];
554
+ videoFrame.bufferType = VeLiveVideoBufferTypePixelBuffer;
555
+ videoFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000);
556
+ videoFrame.pixelBuffer = pixelBuffer;
557
+
558
+ [[self.pusher getMixerManager] sendCustomVideoFrame:videoFrame
559
+ streamId:streamId];
560
+ } @catch (NSException *exception) {
561
+ // Handle exception silently
562
+ }
563
+ }
564
+ }
565
+
566
+ #pragma mark - Helper Methods
567
+
568
+ - (CVPixelBufferRef)pixelBufferFromUIImage:(UIImage *)image {
569
+ if (!image) {
570
+ return NULL;
571
+ }
572
+
573
+ CGImageRef cgImage = image.CGImage;
574
+ if (!cgImage) {
575
+ return NULL;
576
+ }
577
+
578
+ CGFloat width = CGImageGetWidth(cgImage);
579
+ CGFloat height = CGImageGetHeight(cgImage);
580
+
581
+ NSDictionary *options = @{
582
+ (NSString *)kCVPixelBufferCGImageCompatibilityKey : @YES,
583
+ (NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
584
+ (NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{}
585
+ };
586
+
587
+ CVPixelBufferRef pixelBuffer = NULL;
588
+ CVReturn status = CVPixelBufferCreate(
589
+ kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA,
590
+ (__bridge CFDictionaryRef)options, &pixelBuffer);
591
+
592
+ if (status != kCVReturnSuccess || !pixelBuffer) {
593
+ return NULL;
594
+ }
595
+
596
+ CVPixelBufferLockBaseAddress(pixelBuffer, 0);
597
+
598
+ void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);
599
+ size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
600
+
601
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
602
+ CGContextRef context = CGBitmapContextCreate(
603
+ pixelData, width, height, 8, bytesPerRow, colorSpace,
604
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
605
+
606
+ if (!context) {
607
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
608
+ CVPixelBufferRelease(pixelBuffer);
609
+ CGColorSpaceRelease(colorSpace);
610
+ return NULL;
611
+ }
612
+
613
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
614
+
615
+ CGContextRelease(context);
616
+ CGColorSpaceRelease(colorSpace);
617
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
618
+
619
+ return pixelBuffer;
620
+ }
621
+
622
+ @end
623
+
624
+ #pragma mark - VeLiveMixerBitmapCallback Implementation
625
+
626
+ @implementation VeLiveMixerBitmapCallback
627
+
628
+ - (instancetype)initWithStreamId:(int)streamId {
629
+ self = [super init];
630
+ if (self) {
631
+ _streamId = streamId;
632
+ }
633
+ return self;
634
+ }
635
+
636
+ - (void)onBitmapCaptured:(UIImage *)bitmap {
637
+ if (bitmap != nil) {
638
+ [VeLiveMixerHelper sendBitmapToMixerStatic:self.streamId bitmap:bitmap];
639
+ }
640
+ }
641
+
642
+ - (void)onCaptureError:(NSString *)error {
643
+ // Ignore
644
+ }
645
+
646
+ @end