@byteplus/react-native-live-push 1.1.3-rc.2 → 1.1.3-rc.3
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/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +5 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +9 -1
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/TextureMgr.java +3 -3
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/YuvHelper.java +16 -5
- package/ios/VeLiveMixerHelper.m +192 -163
- package/package.json +1 -1
|
@@ -255,7 +255,8 @@ public class MixerView extends FrameLayout {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
long currentTime = System.currentTimeMillis();
|
|
258
|
-
|
|
258
|
+
long minInterval = 33; // 30 FPS ~ 33ms per frame
|
|
259
|
+
if (currentTime - lastCaptureTime < minInterval) {
|
|
259
260
|
return;
|
|
260
261
|
}
|
|
261
262
|
lastCaptureTime = currentTime;
|
|
@@ -316,6 +317,7 @@ public class MixerView extends FrameLayout {
|
|
|
316
317
|
// Call callback first if set (for MixerManager)
|
|
317
318
|
if (bitmapCaptureCallback != null) {
|
|
318
319
|
try {
|
|
320
|
+
// only capture if the callback is set
|
|
319
321
|
bitmapCaptureCallback.onBitmapCaptured(bitmap);
|
|
320
322
|
} catch (Exception e) {
|
|
321
323
|
if (bitmapCaptureCallback != null) {
|
|
@@ -339,6 +341,12 @@ public class MixerView extends FrameLayout {
|
|
|
339
341
|
} catch (IllegalStateException e) {
|
|
340
342
|
// Ignore
|
|
341
343
|
}
|
|
344
|
+
|
|
345
|
+
// if no callback, recycle immediately to avoid accumulation
|
|
346
|
+
if (bitmap != null && !bitmap.isRecycled()) {
|
|
347
|
+
bitmap.recycle();
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
342
350
|
}
|
|
343
351
|
|
|
344
352
|
// Clean up old bitmap
|
|
@@ -137,14 +137,14 @@ public class TextureMgr {
|
|
|
137
137
|
GLThreadManager.getMainGlHandle().post(() -> {
|
|
138
138
|
if (texture > 0) {
|
|
139
139
|
Bitmap processedBitmap = null;
|
|
140
|
+
boolean needRecycle = false;
|
|
140
141
|
try {
|
|
141
142
|
if (bitmap == null || bitmap.isRecycled()) {
|
|
142
143
|
return;
|
|
143
144
|
}
|
|
144
|
-
|
|
145
145
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
146
146
|
processedBitmap = YuvHelper.rotateBitmap(bitmap, 0, false, true);
|
|
147
|
-
|
|
147
|
+
needRecycle = (processedBitmap != null && processedBitmap != bitmap);
|
|
148
148
|
if (processedBitmap != null && !processedBitmap.isRecycled()) {
|
|
149
149
|
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, processedBitmap, 0);
|
|
150
150
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
@@ -158,7 +158,7 @@ public class TextureMgr {
|
|
|
158
158
|
} catch (Exception e) {
|
|
159
159
|
// Ignore
|
|
160
160
|
} finally {
|
|
161
|
-
if (
|
|
161
|
+
if (needRecycle && processedBitmap != null && !processedBitmap.isRecycled()) {
|
|
162
162
|
processedBitmap.recycle();
|
|
163
163
|
}
|
|
164
164
|
}
|
|
@@ -12,13 +12,26 @@ import android.renderscript.Type;
|
|
|
12
12
|
import java.nio.ByteBuffer;
|
|
13
13
|
|
|
14
14
|
public class YuvHelper {
|
|
15
|
+
private static RenderScript globalRS = null;
|
|
16
|
+
public static synchronized RenderScript getGlobalRS(Context context) {
|
|
17
|
+
if (globalRS == null) {
|
|
18
|
+
globalRS = RenderScript.create(context.getApplicationContext());
|
|
19
|
+
}
|
|
20
|
+
return globalRS;
|
|
21
|
+
}
|
|
22
|
+
public static void destroyGlobalRS() {
|
|
23
|
+
if (globalRS != null) {
|
|
24
|
+
globalRS.destroy();
|
|
25
|
+
globalRS = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
15
28
|
public static class NV21ToBitmap {
|
|
16
29
|
private RenderScript rs;
|
|
17
30
|
private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
|
|
18
31
|
private Type.Builder yuvType, rgbaType;
|
|
19
32
|
|
|
20
33
|
public NV21ToBitmap(Context context) {
|
|
21
|
-
rs =
|
|
34
|
+
rs = YuvHelper.getGlobalRS(context);
|
|
22
35
|
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
|
|
23
36
|
yuvType = new Type.Builder(rs, Element.U8(rs));
|
|
24
37
|
rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs));
|
|
@@ -30,10 +43,8 @@ public class YuvHelper {
|
|
|
30
43
|
yuvToRgbIntrinsic.destroy();
|
|
31
44
|
yuvToRgbIntrinsic = null;
|
|
32
45
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
rs = null;
|
|
36
|
-
}
|
|
46
|
+
// 不销毁全局 rs
|
|
47
|
+
// rs = null;
|
|
37
48
|
} catch (Exception e) {
|
|
38
49
|
// Ignore
|
|
39
50
|
}
|
package/ios/VeLiveMixerHelper.m
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "VeLiveMixerHelper.h"
|
|
9
|
-
#include <objc/NSObjCRuntime.h>
|
|
10
|
-
#import <UIKit/UIKit.h>
|
|
11
9
|
#import <TTSDKLivePushRTS/TTSDKLivePushRTS.h>
|
|
10
|
+
#import <UIKit/UIKit.h>
|
|
12
11
|
#import <mach/mach.h>
|
|
13
12
|
#import <mach/task_info.h>
|
|
13
|
+
#include <objc/NSObjCRuntime.h>
|
|
14
14
|
|
|
15
15
|
static NSString *TAG = @"VeLiveMixerHelper";
|
|
16
16
|
|
|
@@ -22,36 +22,42 @@ static NSString *TAG = @"VeLiveMixerHelper";
|
|
|
22
22
|
|
|
23
23
|
@interface VeLiveMixerHelper ()
|
|
24
24
|
|
|
25
|
-
@property(nonatomic, strong)
|
|
26
|
-
|
|
27
|
-
@property(nonatomic, strong)
|
|
28
|
-
|
|
29
|
-
@property
|
|
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;
|
|
30
34
|
|
|
31
35
|
@end
|
|
32
36
|
|
|
33
37
|
#pragma mark - VeLiveMixerBitmapCallback Declaration
|
|
34
38
|
|
|
35
39
|
@interface VeLiveMixerBitmapCallback : NSObject <VeLiveBitmapCaptureCallback>
|
|
36
|
-
@property
|
|
40
|
+
@property(nonatomic, assign) int streamId;
|
|
37
41
|
- (instancetype)initWithStreamId:(int)streamId;
|
|
38
42
|
@end
|
|
39
43
|
|
|
40
44
|
@implementation VeLiveMixerHelper
|
|
41
45
|
|
|
42
46
|
// 对齐Android的static cachedMixedViews
|
|
43
|
-
static NSMutableDictionary<NSString *, VeLiveMixerUIView *> *_cachedMixedViews =
|
|
47
|
+
static NSMutableDictionary<NSString *, VeLiveMixerUIView *> *_cachedMixedViews =
|
|
48
|
+
nil;
|
|
44
49
|
static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释放
|
|
45
50
|
|
|
46
51
|
+ (NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
if (!_cachedMixedViews) {
|
|
53
|
+
_cachedMixedViews = [[NSMutableDictionary alloc] init];
|
|
54
|
+
}
|
|
55
|
+
return _cachedMixedViews;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
+ (void)setCachedMixedViews:
|
|
54
|
-
|
|
58
|
+
+ (void)setCachedMixedViews:
|
|
59
|
+
(NSMutableDictionary<NSString *, VeLiveMixerUIView *> *)cachedMixedViews {
|
|
60
|
+
_cachedMixedViews = cachedMixedViews;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
- (instancetype)init {
|
|
@@ -77,7 +83,7 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
77
83
|
_viewIdToStreamId = [[NSMutableDictionary alloc] init];
|
|
78
84
|
_pendingCallbacks = [[NSMutableDictionary alloc] init];
|
|
79
85
|
_callbacks = [[NSMutableDictionary alloc] init];
|
|
80
|
-
|
|
86
|
+
|
|
81
87
|
currentInstance = self;
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -115,7 +121,7 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
115
121
|
|
|
116
122
|
- (void)setPusher:(VeLivePusher *)pusher {
|
|
117
123
|
_pusher = pusher;
|
|
118
|
-
|
|
124
|
+
|
|
119
125
|
if (currentInstance != self) {
|
|
120
126
|
currentInstance = self;
|
|
121
127
|
}
|
|
@@ -126,30 +132,31 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
126
132
|
if (!self.pusher) {
|
|
127
133
|
return -1;
|
|
128
134
|
}
|
|
129
|
-
|
|
135
|
+
|
|
130
136
|
VeLiveMixerUIView *view = [VeLiveMixerHelper cachedMixedViews][viewId];
|
|
131
137
|
if (![view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
132
138
|
// View not ready, store pending callback
|
|
133
139
|
}
|
|
134
|
-
|
|
140
|
+
|
|
135
141
|
NSNumber *existingStreamId = [self.viewIdToStreamId objectForKey:viewId];
|
|
136
|
-
if(existingStreamId != nil) {
|
|
142
|
+
if (existingStreamId != nil) {
|
|
137
143
|
return [existingStreamId intValue];
|
|
138
144
|
}
|
|
139
|
-
|
|
145
|
+
|
|
140
146
|
int videoStreamId = [[self.pusher getMixerManager] addVideoStream];
|
|
141
|
-
|
|
147
|
+
|
|
142
148
|
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)
|
|
149
|
+
@"x" : viewInfo[@"x"] ?: @(0),
|
|
150
|
+
@"y" : viewInfo[@"y"] ?: @(0),
|
|
151
|
+
@"width" : viewInfo[@"width"] ?: @(0),
|
|
152
|
+
@"height" : viewInfo[@"height"] ?: @(0),
|
|
153
|
+
@"zOrder" : viewInfo[@"zOrder"] ?: @(1),
|
|
154
|
+
@"renderMode" : viewInfo[@"renderMode"] ?: @(0),
|
|
155
|
+
@"streamId" : @(videoStreamId)
|
|
150
156
|
};
|
|
151
|
-
|
|
152
|
-
VeLiveStreamMixDescription *description =
|
|
157
|
+
|
|
158
|
+
VeLiveStreamMixDescription *description =
|
|
159
|
+
[[VeLiveStreamMixDescription alloc] init];
|
|
153
160
|
VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
|
|
154
161
|
videoLayout.streamId = videoStreamId;
|
|
155
162
|
videoLayout.x = [viewInfo[@"x"] floatValue];
|
|
@@ -158,18 +165,18 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
158
165
|
videoLayout.height = [viewInfo[@"height"] floatValue];
|
|
159
166
|
videoLayout.zOrder = [viewInfo[@"zOrder"] intValue];
|
|
160
167
|
videoLayout.renderMode = [viewInfo[@"renderMode"] intValue];
|
|
161
|
-
description.mixVideoStreams = @[videoLayout];
|
|
168
|
+
description.mixVideoStreams = @[ videoLayout ];
|
|
162
169
|
[self.pusher.getMixerManager updateStreamMixDescription:description];
|
|
163
|
-
|
|
170
|
+
|
|
164
171
|
[self.mixedViewLayout setObject:layout forKey:viewId];
|
|
165
172
|
[self.viewIdToStreamId setObject:@(videoStreamId) forKey:viewId];
|
|
166
|
-
|
|
173
|
+
|
|
167
174
|
if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
168
175
|
[VeLiveMixerHelper setupCallbackForMixerView:view streamId:videoStreamId];
|
|
169
176
|
} else {
|
|
170
177
|
[self.pendingCallbacks setObject:@(videoStreamId) forKey:viewId];
|
|
171
178
|
}
|
|
172
|
-
|
|
179
|
+
|
|
173
180
|
return videoStreamId;
|
|
174
181
|
} @catch (NSException *exception) {
|
|
175
182
|
return -1;
|
|
@@ -182,19 +189,24 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
182
189
|
if (!layout) {
|
|
183
190
|
return NO;
|
|
184
191
|
}
|
|
185
|
-
|
|
192
|
+
|
|
186
193
|
NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
|
|
187
194
|
int videoStreamId = [streamIdNumber intValue];
|
|
188
|
-
|
|
195
|
+
|
|
189
196
|
NSMutableDictionary *updatedLayout = [layout mutableCopy];
|
|
190
197
|
[updatedLayout setObject:(viewInfo[@"x"] ?: layout[@"x"]) forKey:@"x"];
|
|
191
198
|
[updatedLayout setObject:(viewInfo[@"y"] ?: layout[@"y"]) forKey:@"y"];
|
|
192
|
-
[updatedLayout setObject:(viewInfo[@"width"] ?: layout[@"width"])
|
|
193
|
-
|
|
194
|
-
[updatedLayout setObject:(viewInfo[@"
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
[updatedLayout setObject:(viewInfo[@"width"] ?: layout[@"width"])
|
|
200
|
+
forKey:@"width"];
|
|
201
|
+
[updatedLayout setObject:(viewInfo[@"height"] ?: layout[@"height"])
|
|
202
|
+
forKey:@"height"];
|
|
203
|
+
[updatedLayout setObject:(viewInfo[@"zOrder"] ?: layout[@"zOrder"])
|
|
204
|
+
forKey:@"zOrder"];
|
|
205
|
+
[updatedLayout setObject:(viewInfo[@"renderMode"] ?: layout[@"renderMode"])
|
|
206
|
+
forKey:@"renderMode"];
|
|
207
|
+
|
|
208
|
+
VeLiveStreamMixDescription *description =
|
|
209
|
+
[[VeLiveStreamMixDescription alloc] init];
|
|
198
210
|
VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init];
|
|
199
211
|
videoLayout.streamId = videoStreamId;
|
|
200
212
|
videoLayout.x = [updatedLayout[@"x"] floatValue];
|
|
@@ -203,14 +215,15 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
203
215
|
videoLayout.height = [updatedLayout[@"height"] floatValue];
|
|
204
216
|
videoLayout.zOrder = [updatedLayout[@"zOrder"] intValue];
|
|
205
217
|
videoLayout.renderMode = [updatedLayout[@"renderMode"] intValue];
|
|
206
|
-
description.mixVideoStreams = @[videoLayout];
|
|
218
|
+
description.mixVideoStreams = @[ videoLayout ];
|
|
207
219
|
[self.pusher.getMixerManager updateStreamMixDescription:description];
|
|
208
|
-
|
|
220
|
+
|
|
209
221
|
[self.mixedViewLayout setObject:updatedLayout forKey:viewId];
|
|
210
|
-
|
|
211
|
-
VeLiveMixerUIView *mixerView =
|
|
222
|
+
|
|
223
|
+
VeLiveMixerUIView *mixerView =
|
|
224
|
+
[[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
|
|
212
225
|
[mixerView performCaptureWithTrigger:@"update"];
|
|
213
|
-
|
|
226
|
+
|
|
214
227
|
return YES;
|
|
215
228
|
} @catch (NSException *exception) {
|
|
216
229
|
return NO;
|
|
@@ -223,26 +236,27 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
223
236
|
if (!layout) {
|
|
224
237
|
return NO;
|
|
225
238
|
}
|
|
226
|
-
|
|
239
|
+
|
|
227
240
|
NSNumber *streamIdNumber = [self.viewIdToStreamId objectForKey:viewId];
|
|
228
241
|
if (streamIdNumber) {
|
|
229
242
|
int streamId = [streamIdNumber intValue];
|
|
230
243
|
[self.pusher.getMixerManager removeVideoStream:streamId];
|
|
231
|
-
|
|
244
|
+
|
|
232
245
|
NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
|
|
233
246
|
[self.callbacks removeObjectForKey:callbackKey];
|
|
234
247
|
}
|
|
235
|
-
|
|
248
|
+
|
|
236
249
|
[self.viewIdToStreamId removeObjectForKey:viewId];
|
|
237
250
|
[self.pendingCallbacks removeObjectForKey:viewId];
|
|
238
|
-
|
|
239
|
-
VeLiveMixerUIView *view =
|
|
251
|
+
|
|
252
|
+
VeLiveMixerUIView *view =
|
|
253
|
+
[[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
|
|
240
254
|
if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
241
255
|
view.bitmapCaptureCallback = nil;
|
|
242
256
|
}
|
|
243
|
-
|
|
257
|
+
|
|
244
258
|
[self.mixedViewLayout removeObjectForKey:viewId];
|
|
245
|
-
|
|
259
|
+
|
|
246
260
|
return YES;
|
|
247
261
|
} @catch (NSException *exception) {
|
|
248
262
|
return NO;
|
|
@@ -250,7 +264,8 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
- (void)captureView:(NSString *)viewId {
|
|
253
|
-
VeLiveMixerUIView *view =
|
|
267
|
+
VeLiveMixerUIView *view =
|
|
268
|
+
[[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
|
|
254
269
|
if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
255
270
|
[view performCaptureWithTrigger:@"manual"];
|
|
256
271
|
}
|
|
@@ -258,55 +273,64 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
258
273
|
|
|
259
274
|
#pragma mark - Static Methods
|
|
260
275
|
|
|
261
|
-
+ (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView
|
|
276
|
+
+ (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView
|
|
277
|
+
streamId:(int)streamId {
|
|
262
278
|
if (mixerView == nil) {
|
|
263
279
|
return;
|
|
264
280
|
}
|
|
265
|
-
|
|
281
|
+
|
|
266
282
|
VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
|
|
267
283
|
if (!instance) {
|
|
268
284
|
return;
|
|
269
285
|
}
|
|
270
|
-
|
|
271
|
-
VeLiveMixerBitmapCallback *callback =
|
|
272
|
-
|
|
286
|
+
|
|
287
|
+
VeLiveMixerBitmapCallback *callback =
|
|
288
|
+
[[VeLiveMixerBitmapCallback alloc] initWithStreamId:streamId];
|
|
289
|
+
|
|
273
290
|
NSString *callbackKey = [NSString stringWithFormat:@"%d", streamId];
|
|
274
291
|
[instance.callbacks setObject:callback forKey:callbackKey];
|
|
275
|
-
|
|
292
|
+
|
|
276
293
|
mixerView.bitmapCaptureCallback = callback;
|
|
277
|
-
|
|
278
|
-
dispatch_after(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
294
|
+
|
|
295
|
+
dispatch_after(
|
|
296
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)),
|
|
297
|
+
dispatch_get_main_queue(), ^{
|
|
298
|
+
@try {
|
|
299
|
+
if (mixerView && mixerView.bitmapCaptureCallback) {
|
|
300
|
+
VeLiveMixerHelper *instance =
|
|
301
|
+
[VeLiveMixerHelper findActiveMixerManager];
|
|
302
|
+
if (instance && instance.pusher) {
|
|
303
|
+
@try {
|
|
304
|
+
NSString *viewId = mixerView.viewId;
|
|
305
|
+
NSDictionary *layout =
|
|
306
|
+
[instance.mixedViewLayout objectForKey:viewId];
|
|
307
|
+
if (layout) {
|
|
308
|
+
VeLiveStreamMixDescription *description =
|
|
309
|
+
[[VeLiveStreamMixDescription alloc] init];
|
|
310
|
+
VeLiveMixVideoLayout *videoLayout =
|
|
311
|
+
[[VeLiveMixVideoLayout alloc] init];
|
|
312
|
+
videoLayout.streamId = streamId;
|
|
313
|
+
videoLayout.x = [layout[@"x"] floatValue];
|
|
314
|
+
videoLayout.y = [layout[@"y"] floatValue];
|
|
315
|
+
videoLayout.width = [layout[@"width"] floatValue];
|
|
316
|
+
videoLayout.height = [layout[@"height"] floatValue];
|
|
317
|
+
videoLayout.zOrder = [layout[@"zOrder"] intValue];
|
|
318
|
+
videoLayout.renderMode = [layout[@"renderMode"] intValue];
|
|
319
|
+
description.mixVideoStreams = @[ videoLayout ];
|
|
320
|
+
[instance.pusher.getMixerManager
|
|
321
|
+
updateStreamMixDescription:description];
|
|
322
|
+
}
|
|
323
|
+
} @catch (NSException *e) {
|
|
324
|
+
// Ignore
|
|
325
|
+
}
|
|
298
326
|
}
|
|
299
|
-
|
|
300
|
-
|
|
327
|
+
|
|
328
|
+
[mixerView performCaptureWithTrigger:@"update"];
|
|
301
329
|
}
|
|
330
|
+
} @catch (NSException *exception) {
|
|
331
|
+
// Ignore
|
|
302
332
|
}
|
|
303
|
-
|
|
304
|
-
[mixerView performCaptureWithTrigger:@"update"];
|
|
305
|
-
}
|
|
306
|
-
} @catch (NSException *exception) {
|
|
307
|
-
// Ignore
|
|
308
|
-
}
|
|
309
|
-
});
|
|
333
|
+
});
|
|
310
334
|
}
|
|
311
335
|
|
|
312
336
|
+ (void)sendBitmapToMixerStatic:(int)streamId bitmap:(UIImage *)bitmap {
|
|
@@ -320,15 +344,18 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
320
344
|
return currentInstance;
|
|
321
345
|
}
|
|
322
346
|
|
|
323
|
-
+ (void)onViewReady:(NSString *)viewId
|
|
347
|
+
+ (void)onViewReady:(NSString *)viewId
|
|
348
|
+
mixerView:(VeLiveMixerUIView *)mixerView {
|
|
324
349
|
@try {
|
|
325
350
|
VeLiveMixerHelper *instance = [VeLiveMixerHelper findActiveMixerManager];
|
|
326
351
|
if (instance != nil) {
|
|
327
|
-
NSNumber *streamIdNumber =
|
|
352
|
+
NSNumber *streamIdNumber =
|
|
353
|
+
[instance.pendingCallbacks objectForKey:viewId];
|
|
328
354
|
if (streamIdNumber != nil) {
|
|
329
355
|
int streamId = [streamIdNumber intValue];
|
|
330
356
|
[instance.pendingCallbacks removeObjectForKey:viewId];
|
|
331
|
-
[VeLiveMixerHelper setupCallbackForMixerView:mixerView
|
|
357
|
+
[VeLiveMixerHelper setupCallbackForMixerView:mixerView
|
|
358
|
+
streamId:streamId];
|
|
332
359
|
}
|
|
333
360
|
}
|
|
334
361
|
} @catch (NSException *exception) {
|
|
@@ -338,36 +365,42 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
338
365
|
|
|
339
366
|
- (void)cleanup {
|
|
340
367
|
[self stopMemoryMonitoring];
|
|
341
|
-
|
|
368
|
+
|
|
342
369
|
for (NSString *viewId in self.mixedViewLayout.allKeys) {
|
|
343
|
-
VeLiveMixerUIView *view =
|
|
370
|
+
VeLiveMixerUIView *view =
|
|
371
|
+
[[VeLiveMixerHelper cachedMixedViews] objectForKey:viewId];
|
|
344
372
|
if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
345
373
|
view.bitmapCaptureCallback = nil;
|
|
346
374
|
}
|
|
347
375
|
}
|
|
348
|
-
|
|
376
|
+
|
|
349
377
|
[self.mixedViewLayout removeAllObjects];
|
|
350
378
|
[self.pendingCallbacks removeAllObjects];
|
|
351
379
|
[self.callbacks removeAllObjects];
|
|
352
|
-
|
|
380
|
+
|
|
353
381
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
|
354
382
|
@autoreleasepool {
|
|
355
383
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
356
384
|
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
|
|
357
385
|
[[NSURLCache sharedURLCache] setMemoryCapacity:10 * 1024 * 1024];
|
|
358
|
-
|
|
359
|
-
[[NSNotificationCenter defaultCenter]
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
386
|
+
|
|
387
|
+
[[NSNotificationCenter defaultCenter]
|
|
388
|
+
postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
|
|
389
|
+
object:nil];
|
|
390
|
+
|
|
391
|
+
dispatch_after(
|
|
392
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
|
|
393
|
+
dispatch_get_main_queue(),
|
|
394
|
+
^{
|
|
395
|
+
// Final cleanup completed
|
|
396
|
+
});
|
|
364
397
|
}
|
|
365
398
|
});
|
|
366
399
|
}
|
|
367
400
|
|
|
368
401
|
- (void)dealloc {
|
|
369
402
|
[self cleanup];
|
|
370
|
-
|
|
403
|
+
|
|
371
404
|
if (currentInstance == self) {
|
|
372
405
|
currentInstance = nil;
|
|
373
406
|
}
|
|
@@ -376,11 +409,12 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
376
409
|
#pragma mark - Memory Monitoring
|
|
377
410
|
|
|
378
411
|
- (void)startMemoryMonitoring {
|
|
379
|
-
self.memoryMonitorTimer =
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
412
|
+
self.memoryMonitorTimer =
|
|
413
|
+
[NSTimer scheduledTimerWithTimeInterval:30.0
|
|
414
|
+
target:self
|
|
415
|
+
selector:@selector(checkMemoryUsage)
|
|
416
|
+
userInfo:nil
|
|
417
|
+
repeats:YES];
|
|
384
418
|
}
|
|
385
419
|
|
|
386
420
|
- (void)stopMemoryMonitoring {
|
|
@@ -394,11 +428,12 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
394
428
|
@autoreleasepool {
|
|
395
429
|
struct task_basic_info info;
|
|
396
430
|
mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
|
|
397
|
-
kern_return_t kerr =
|
|
398
|
-
|
|
431
|
+
kern_return_t kerr =
|
|
432
|
+
task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
|
|
433
|
+
|
|
399
434
|
if (kerr == KERN_SUCCESS) {
|
|
400
435
|
double memoryUsageMB = info.resident_size / 1024.0 / 1024.0;
|
|
401
|
-
|
|
436
|
+
|
|
402
437
|
if (memoryUsageMB > 200.0) {
|
|
403
438
|
[self performMemoryCleanup];
|
|
404
439
|
}
|
|
@@ -415,20 +450,24 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
415
450
|
if ([view isKindOfClass:[VeLiveMixerUIView class]]) {
|
|
416
451
|
if ([view.captureMode isEqualToString:@"realtime"]) {
|
|
417
452
|
[view stopCapture];
|
|
418
|
-
|
|
419
|
-
dispatch_after(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
453
|
+
|
|
454
|
+
dispatch_after(
|
|
455
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
|
|
456
|
+
dispatch_get_main_queue(), ^{
|
|
457
|
+
if (view && [cachedViews objectForKey:viewId] == view) {
|
|
458
|
+
[view startCapture];
|
|
459
|
+
}
|
|
460
|
+
});
|
|
424
461
|
}
|
|
425
462
|
}
|
|
426
463
|
}
|
|
427
|
-
|
|
464
|
+
|
|
428
465
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
|
429
466
|
[[NSURLCache sharedURLCache] setMemoryCapacity:5 * 1024 * 1024];
|
|
430
|
-
|
|
431
|
-
[[NSNotificationCenter defaultCenter]
|
|
467
|
+
|
|
468
|
+
[[NSNotificationCenter defaultCenter]
|
|
469
|
+
postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
|
|
470
|
+
object:nil];
|
|
432
471
|
}
|
|
433
472
|
});
|
|
434
473
|
}
|
|
@@ -440,34 +479,26 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
440
479
|
if (!self.pusher || !bitmap) {
|
|
441
480
|
return;
|
|
442
481
|
}
|
|
443
|
-
|
|
482
|
+
|
|
444
483
|
CVPixelBufferRef pixelBuffer = NULL;
|
|
445
|
-
|
|
484
|
+
|
|
446
485
|
@try {
|
|
447
486
|
pixelBuffer = [self pixelBufferFromUIImage:bitmap];
|
|
448
487
|
if (!pixelBuffer) {
|
|
449
488
|
return;
|
|
450
489
|
}
|
|
451
|
-
|
|
490
|
+
|
|
452
491
|
VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init];
|
|
453
492
|
videoFrame.bufferType = VeLiveVideoBufferTypePixelBuffer;
|
|
454
493
|
videoFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000);
|
|
455
494
|
videoFrame.pixelBuffer = pixelBuffer;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
CVPixelBufferRelease(bufferToRelease);
|
|
462
|
-
bufferToRelease = NULL;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}];
|
|
466
|
-
|
|
467
|
-
[[self.pusher getMixerManager] sendCustomVideoFrame:videoFrame streamId:streamId];
|
|
468
|
-
|
|
495
|
+
|
|
496
|
+
[[self.pusher getMixerManager] sendCustomVideoFrame:videoFrame
|
|
497
|
+
streamId:streamId];
|
|
498
|
+
CVPixelBufferRelease(pixelBuffer);
|
|
499
|
+
|
|
469
500
|
pixelBuffer = NULL;
|
|
470
|
-
|
|
501
|
+
|
|
471
502
|
} @catch (NSException *exception) {
|
|
472
503
|
if (pixelBuffer) {
|
|
473
504
|
CVPixelBufferRelease(pixelBuffer);
|
|
@@ -483,55 +514,53 @@ static VeLiveMixerHelper *currentInstance = nil; // 强引用,防止被ARC释
|
|
|
483
514
|
if (!image) {
|
|
484
515
|
return NULL;
|
|
485
516
|
}
|
|
486
|
-
|
|
517
|
+
|
|
487
518
|
CGImageRef cgImage = image.CGImage;
|
|
488
519
|
if (!cgImage) {
|
|
489
520
|
return NULL;
|
|
490
521
|
}
|
|
491
|
-
|
|
522
|
+
|
|
492
523
|
CGFloat width = CGImageGetWidth(cgImage);
|
|
493
524
|
CGFloat height = CGImageGetHeight(cgImage);
|
|
494
|
-
|
|
525
|
+
|
|
495
526
|
NSDictionary *options = @{
|
|
496
|
-
(NSString *)kCVPixelBufferCGImageCompatibilityKey: @YES,
|
|
497
|
-
(NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES,
|
|
498
|
-
(NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}
|
|
527
|
+
(NSString *)kCVPixelBufferCGImageCompatibilityKey : @YES,
|
|
528
|
+
(NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
|
|
529
|
+
(NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{}
|
|
499
530
|
};
|
|
500
|
-
|
|
531
|
+
|
|
501
532
|
CVPixelBufferRef pixelBuffer = NULL;
|
|
502
|
-
CVReturn status = CVPixelBufferCreate(
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
&pixelBuffer);
|
|
507
|
-
|
|
533
|
+
CVReturn status = CVPixelBufferCreate(
|
|
534
|
+
kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA,
|
|
535
|
+
(__bridge CFDictionaryRef)options, &pixelBuffer);
|
|
536
|
+
|
|
508
537
|
if (status != kCVReturnSuccess || !pixelBuffer) {
|
|
509
538
|
return NULL;
|
|
510
539
|
}
|
|
511
|
-
|
|
540
|
+
|
|
512
541
|
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
|
513
|
-
|
|
542
|
+
|
|
514
543
|
void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);
|
|
515
544
|
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
|
|
516
|
-
|
|
545
|
+
|
|
517
546
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
518
|
-
CGContextRef context = CGBitmapContextCreate(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
547
|
+
CGContextRef context = CGBitmapContextCreate(
|
|
548
|
+
pixelData, width, height, 8, bytesPerRow, colorSpace,
|
|
549
|
+
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
|
|
550
|
+
|
|
522
551
|
if (!context) {
|
|
523
552
|
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
|
524
553
|
CVPixelBufferRelease(pixelBuffer);
|
|
525
554
|
CGColorSpaceRelease(colorSpace);
|
|
526
555
|
return NULL;
|
|
527
556
|
}
|
|
528
|
-
|
|
557
|
+
|
|
529
558
|
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
|
|
530
|
-
|
|
559
|
+
|
|
531
560
|
CGContextRelease(context);
|
|
532
561
|
CGColorSpaceRelease(colorSpace);
|
|
533
562
|
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
|
534
|
-
|
|
563
|
+
|
|
535
564
|
return pixelBuffer;
|
|
536
565
|
}
|
|
537
566
|
|