tirtc-react-native 2.3.1 → 2.4.0-alpha.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.
Files changed (77) hide show
  1. package/README.md +23 -7
  2. package/TiRtcReactNative.podspec +1 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/AndroidManifest.xml +2 -1
  5. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +585 -28
  6. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCleanup.kt +115 -0
  7. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +35 -8
  8. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +20 -0
  9. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +62 -0
  10. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeModuleSupport.kt +6 -6
  11. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +27 -0
  12. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeStoreSupport.kt +64 -0
  13. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoOutputViewManager.kt +17 -2
  14. package/ios/TiRtcReactNative+Conn.mm +173 -2
  15. package/ios/TiRtcReactNative+Input.mm +18 -4
  16. package/ios/TiRtcReactNative+Output.mm +41 -4
  17. package/ios/TiRtcReactNative+Private.h +4 -0
  18. package/ios/TiRtcReactNative+Store.mm +496 -0
  19. package/ios/TiRtcReactNative.h +2 -1
  20. package/ios/TiRtcReactNative.mm +109 -1
  21. package/ios/TiRtcReactNativeEventBinder.h +8 -0
  22. package/ios/TiRtcReactNativeEventBinder.mm +104 -0
  23. package/ios/TiRtcReactNativeEventSink.h +4 -0
  24. package/ios/TiRtcReactNativeEventSink.mm +20 -0
  25. package/ios/TiRtcReactNativeRegistry.h +5 -0
  26. package/ios/TiRtcReactNativeRegistry.mm +43 -0
  27. package/ios/TiRtcVideoOutputView.mm +11 -0
  28. package/lib/module/audio_input.js +33 -17
  29. package/lib/module/audio_input.js.map +1 -1
  30. package/lib/module/audio_output.js +27 -10
  31. package/lib/module/audio_output.js.map +1 -1
  32. package/lib/module/conn.js +31 -7
  33. package/lib/module/conn.js.map +1 -1
  34. package/lib/module/errors.js +4 -3
  35. package/lib/module/errors.js.map +1 -1
  36. package/lib/module/index.js +2 -0
  37. package/lib/module/index.js.map +1 -1
  38. package/lib/module/logging.js +9 -1
  39. package/lib/module/logging.js.map +1 -1
  40. package/lib/module/logging_internal.js +27 -0
  41. package/lib/module/logging_internal.js.map +1 -0
  42. package/lib/module/recording.js +210 -0
  43. package/lib/module/recording.js.map +1 -0
  44. package/lib/module/runtime.js +13 -5
  45. package/lib/module/runtime.js.map +1 -1
  46. package/lib/module/specs/NativeTiRtc.js.map +1 -1
  47. package/lib/module/store.js +698 -0
  48. package/lib/module/store.js.map +1 -0
  49. package/lib/module/video_input.js +40 -22
  50. package/lib/module/video_input.js.map +1 -1
  51. package/lib/module/video_output.js +51 -10
  52. package/lib/module/video_output.js.map +1 -1
  53. package/lib/typescript/audio_input.d.ts +1 -0
  54. package/lib/typescript/conn.d.ts +2 -0
  55. package/lib/typescript/errors.d.ts +3 -2
  56. package/lib/typescript/index.d.ts +4 -0
  57. package/lib/typescript/logging_internal.d.ts +2 -0
  58. package/lib/typescript/recording.d.ts +53 -0
  59. package/lib/typescript/runtime.d.ts +2 -0
  60. package/lib/typescript/specs/NativeTiRtc.d.ts +88 -0
  61. package/lib/typescript/store.d.ts +202 -0
  62. package/lib/typescript/video_input.d.ts +1 -0
  63. package/lib/typescript/video_output.d.ts +3 -0
  64. package/package.json +1 -1
  65. package/src/audio_input.ts +85 -59
  66. package/src/audio_output.ts +30 -17
  67. package/src/conn.ts +46 -28
  68. package/src/errors.ts +4 -3
  69. package/src/index.ts +54 -0
  70. package/src/logging.ts +7 -1
  71. package/src/logging_internal.ts +23 -0
  72. package/src/recording.ts +271 -0
  73. package/src/runtime.ts +14 -5
  74. package/src/specs/NativeTiRtc.ts +123 -0
  75. package/src/store.ts +760 -0
  76. package/src/video_input.ts +97 -62
  77. package/src/video_output.ts +54 -16
@@ -0,0 +1,496 @@
1
+ #import "TiRtcReactNative+Private.h"
2
+
3
+ #import "TiRtcReactNativeErrors.h"
4
+ #import "TiRtcReactNativeMaps.h"
5
+
6
+ #pragma clang diagnostic push
7
+ #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
8
+
9
+ static NSInteger TiRtcReactNativeStoreReplaySpeedValue(TiStoreReplaySpeed speed) {
10
+ switch (speed) {
11
+ case TiStoreReplaySpeedX1: return 0;
12
+ case TiStoreReplaySpeedX2: return 1;
13
+ case TiStoreReplaySpeedX4: return 2;
14
+ case TiStoreReplaySpeedX8: return 3;
15
+ }
16
+ return 0;
17
+ }
18
+
19
+ static BOOL TiRtcReactNativeStoreReplaySpeedFromValue(NSInteger value, TiStoreReplaySpeed* speed) {
20
+ switch (value) {
21
+ case 0: *speed = TiStoreReplaySpeedX1; return YES;
22
+ case 1: *speed = TiStoreReplaySpeedX2; return YES;
23
+ case 2: *speed = TiStoreReplaySpeedX4; return YES;
24
+ case 3: *speed = TiStoreReplaySpeedX8; return YES;
25
+ default: return NO;
26
+ }
27
+ }
28
+
29
+ static NSString* TiRtcReactNativeStoreAudioState(TiStoreAudioOutputState state) {
30
+ switch (state) {
31
+ case TiStoreAudioOutputStateIdle: return @"idle";
32
+ case TiStoreAudioOutputStateBuffering: return @"buffering";
33
+ case TiStoreAudioOutputStatePlaying: return @"playing";
34
+ case TiStoreAudioOutputStateFailed: return @"failed";
35
+ case TiStoreAudioOutputStatePaused: return @"paused";
36
+ case TiStoreAudioOutputStateCompleted: return @"completed";
37
+ }
38
+ return @"failed";
39
+ }
40
+
41
+ static NSString* TiRtcReactNativeStoreVideoState(TiStoreVideoOutputState state) {
42
+ switch (state) {
43
+ case TiStoreVideoOutputStateIdle: return @"idle";
44
+ case TiStoreVideoOutputStateBuffering: return @"buffering";
45
+ case TiStoreVideoOutputStateRendering: return @"rendering";
46
+ case TiStoreVideoOutputStateFailed: return @"failed";
47
+ case TiStoreVideoOutputStatePaused: return @"paused";
48
+ case TiStoreVideoOutputStateCompleted: return @"completed";
49
+ }
50
+ return @"failed";
51
+ }
52
+
53
+ static NSDictionary* TiRtcReactNativeStoreMp4Result(
54
+ TiRtcReactNativeRegistry* registry, TiStoreRecordingResult* result) {
55
+ NSInteger fileId = result.file == nil ? 0 : [registry addTransientObject:result.file];
56
+ return @{
57
+ @"code" : @(TiRtcReactNativeNormalizeDarwin(result.code)),
58
+ @"fileId" : fileId == 0 ? (id)kCFNull : @(fileId),
59
+ @"filePath" : result.file.path ?: (id)kCFNull,
60
+ @"durationMs" : result.file == nil ? (id)kCFNull : @(result.file.durationMs),
61
+ };
62
+ }
63
+
64
+ @interface TiRtcReactNativeStoreExportBridge : NSObject <TiRtcReactNativeReloadTask>
65
+ @property(nonatomic, strong) TiStoreExportTask* task;
66
+ @property(nonatomic, assign) NSInteger objectId;
67
+ - (instancetype)initWithRegistry:(TiRtcReactNativeRegistry*)registry;
68
+ - (void)complete:(TiStoreRecordingResult*)result;
69
+ - (void)await:(RCTPromiseResolveBlock)resolve;
70
+ @end
71
+
72
+ @implementation TiRtcReactNativeStoreExportBridge {
73
+ TiRtcReactNativeRegistry* _registry;
74
+ TiStoreRecordingResult* _result;
75
+ NSMutableArray<RCTPromiseResolveBlock>* _waiters;
76
+ BOOL _invalidated;
77
+ void (^_invalidationCompletion)(id file);
78
+ }
79
+
80
+ - (instancetype)initWithRegistry:(TiRtcReactNativeRegistry*)registry {
81
+ if (self = [super init]) {
82
+ _registry = registry;
83
+ _waiters = [NSMutableArray new];
84
+ }
85
+ return self;
86
+ }
87
+
88
+ - (void)complete:(TiStoreRecordingResult*)result {
89
+ NSArray<RCTPromiseResolveBlock>* waiters;
90
+ void (^invalidationCompletion)(id file);
91
+ @synchronized(self) {
92
+ if (_result != nil) return;
93
+ _result = result;
94
+ waiters = _invalidated ? @[] : [_waiters copy];
95
+ [_waiters removeAllObjects];
96
+ invalidationCompletion = _invalidationCompletion;
97
+ _invalidationCompletion = nil;
98
+ }
99
+ for (RCTPromiseResolveBlock resolve in waiters) {
100
+ resolve(TiRtcReactNativeStoreMp4Result(_registry, result));
101
+ }
102
+ if (waiters.count > 0 && self.objectId > 0) [_registry releaseObject:self.objectId];
103
+ if (invalidationCompletion != nil) invalidationCompletion(result.file);
104
+ }
105
+
106
+ - (void)await:(RCTPromiseResolveBlock)resolve {
107
+ TiStoreRecordingResult* result;
108
+ @synchronized(self) {
109
+ result = _result;
110
+ if (result == nil) {
111
+ [_waiters addObject:[resolve copy]];
112
+ return;
113
+ }
114
+ }
115
+ resolve(TiRtcReactNativeStoreMp4Result(_registry, result));
116
+ if (self.objectId > 0) [_registry releaseObject:self.objectId];
117
+ }
118
+
119
+ - (void)stopForReactNativeInvalidation:(void (^)(id file))completion {
120
+ TiStoreRecordingResult* result;
121
+ @synchronized(self) {
122
+ _invalidated = YES;
123
+ [_waiters removeAllObjects];
124
+ result = _result;
125
+ if (result == nil) _invalidationCompletion = [completion copy];
126
+ }
127
+ if (result != nil) {
128
+ completion(result.file);
129
+ return;
130
+ }
131
+ int32_t code = [self.task stop];
132
+ if (code != 0) {
133
+ @synchronized(self) {
134
+ _invalidationCompletion = nil;
135
+ }
136
+ completion(nil);
137
+ }
138
+ }
139
+
140
+ @end
141
+
142
+ @implementation TiRtcReactNative (Store)
143
+
144
+ - (void)storeInitialize:(JS::NativeTiRtc::NativeInitOptions&)options
145
+ resolve:(RCTPromiseResolveBlock)resolve
146
+ reject:(RCTPromiseRejectBlock)reject {
147
+ int32_t code = [TiStore initializeWithAppId:options.appId() ?: @""
148
+ endpoint:options.endpoint() ?: @""
149
+ consoleLogEnabled:options.consoleLogEnabled()];
150
+ resolve(@(TiRtcReactNativeNormalizeDarwin(code)));
151
+ }
152
+
153
+ - (NSNumber*)storeShutdown {
154
+ return @(TiRtcReactNativeNormalizeDarwin([TiStore shutdown]));
155
+ }
156
+
157
+ - (NSDictionary*)storeCreate:(NSString*)token {
158
+ return [self
159
+ registerObject:^id { return [[TiStore alloc] initWithToken:token]; }
160
+ wire:^id(NSInteger objectId, id object) {
161
+ return [[self eventBinder] wireStore:(TiStore*)object objectId:objectId];
162
+ }];
163
+ }
164
+
165
+ - (NSNumber*)storeUpdateToken:(double)objectId token:(NSString*)token {
166
+ id object = [[self registry] objectForId:(NSInteger)objectId];
167
+ return [object isKindOfClass:[TiStore class]]
168
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStore*)object updateToken:token]))
169
+ : @(TiRtcReactNativeErrorObjectReleased);
170
+ }
171
+
172
+ - (void)storeListRecordings:(double)objectId
173
+ startTimeMs:(double)startTimeMs
174
+ endTimeMs:(double)endTimeMs
175
+ resolve:(RCTPromiseResolveBlock)resolve
176
+ reject:(RCTPromiseRejectBlock)reject {
177
+ id object = [[self registry] objectForId:(NSInteger)objectId];
178
+ if (![object isKindOfClass:[TiStore class]]) {
179
+ resolve(@{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"recordings" : @[]});
180
+ return;
181
+ }
182
+ TiStore* store = (TiStore*)object;
183
+ dispatch_async(dispatch_get_main_queue(), ^{
184
+ [store listRecordingsWithStartTimeMs:(int64_t)startTimeMs
185
+ endTimeMs:(int64_t)endTimeMs
186
+ completion:^(TiStoreRecordingRangesResult* result) {
187
+ NSMutableArray* ranges = [NSMutableArray arrayWithCapacity:result.recordings.count];
188
+ for (TiStoreRecordingRange* range in result.recordings) {
189
+ [ranges addObject:@{@"startTimeMs" : @(range.startTimeMs), @"endTimeMs" : @(range.endTimeMs)}];
190
+ }
191
+ resolve(@{@"code" : @(TiRtcReactNativeNormalizeDarwin(result.code)), @"recordings" : ranges});
192
+ }];
193
+ });
194
+ }
195
+
196
+ - (void)storeListRecordingDays:(double)objectId
197
+ startDate:(NSString*)startDate
198
+ endDate:(NSString*)endDate
199
+ timeZoneId:(NSString*)timeZoneId
200
+ resolve:(RCTPromiseResolveBlock)resolve
201
+ reject:(RCTPromiseRejectBlock)reject {
202
+ id object = [[self registry] objectForId:(NSInteger)objectId];
203
+ if (![object isKindOfClass:[TiStore class]]) {
204
+ resolve(@{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"days" : @[]});
205
+ return;
206
+ }
207
+ TiStore* store = (TiStore*)object;
208
+ dispatch_async(dispatch_get_main_queue(), ^{
209
+ [store listRecordingDaysWithStartDate:startDate
210
+ endDate:endDate
211
+ timeZoneId:timeZoneId
212
+ completion:^(TiStoreRecordingDaysResult* result) {
213
+ NSMutableArray* days = [NSMutableArray arrayWithCapacity:result.days.count];
214
+ for (TiStoreRecordingDay* day in result.days) {
215
+ [days addObject:@{@"date" : day.date, @"hasRecording" : @(day.hasRecording)}];
216
+ }
217
+ resolve(@{@"code" : @(TiRtcReactNativeNormalizeDarwin(result.code)), @"days" : days});
218
+ }];
219
+ });
220
+ }
221
+
222
+ - (NSDictionary*)storeCreateReplay:(double)objectId {
223
+ id object = [[self registry] objectForId:(NSInteger)objectId];
224
+ if (![object isKindOfClass:[TiStore class]]) {
225
+ return TiRtcReactNativeCreateResult(TiRtcReactNativeErrorObjectReleased, 0, 0, nil);
226
+ }
227
+ return [self
228
+ registerObject:^id { return [(TiStore*)object createReplay]; }
229
+ wire:^id(NSInteger replayId, id replay) {
230
+ return [[self eventBinder] wireStoreReplay:(TiStoreReplay*)replay objectId:replayId];
231
+ }];
232
+ }
233
+
234
+ - (NSDictionary*)storeExportRecording:(double)objectId
235
+ startTimeMs:(double)startTimeMs
236
+ endTimeMs:(double)endTimeMs
237
+ videoChannelId:(double)videoChannelId
238
+ audioChannelId:(NSNumber*)audioChannelId {
239
+ id object = [[self registry] objectForId:(NSInteger)objectId];
240
+ if (![object isKindOfClass:[TiStore class]]) {
241
+ return @{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"taskId" : (id)kCFNull};
242
+ }
243
+ TiRtcReactNativeStoreExportBridge* bridge =
244
+ [[TiRtcReactNativeStoreExportBridge alloc] initWithRegistry:[self registry]];
245
+ TiStoreExportRequest* request = [[TiStoreExportRequest alloc]
246
+ initWithStartTimeMs:(int64_t)startTimeMs
247
+ endTimeMs:(int64_t)endTimeMs
248
+ videoChannelId:(NSInteger)videoChannelId
249
+ audioChannelId:audioChannelId];
250
+ TiStoreExportTaskStartResult* started = [(TiStore*)object
251
+ exportRecording:request
252
+ progress:nil
253
+ completion:^(TiStoreRecordingResult* result) { [bridge complete:result]; }];
254
+ if (started.code != 0 || started.task == nil) {
255
+ return @{@"code" : @(TiRtcReactNativeNormalizeDarwin(started.code)), @"taskId" : (id)kCFNull};
256
+ }
257
+ bridge.task = started.task;
258
+ bridge.objectId = [[self registry] addObject:bridge];
259
+ return @{@"code" : @0, @"taskId" : @(bridge.objectId)};
260
+ }
261
+
262
+ - (NSNumber*)storeExportProgress:(double)objectId {
263
+ id object = [[self registry] objectForId:(NSInteger)objectId];
264
+ return [object isKindOfClass:[TiRtcReactNativeStoreExportBridge class]]
265
+ ? @(((TiRtcReactNativeStoreExportBridge*)object).task.progress)
266
+ : @0;
267
+ }
268
+
269
+ - (void)storeExportResult:(double)objectId
270
+ resolve:(RCTPromiseResolveBlock)resolve
271
+ reject:(RCTPromiseRejectBlock)reject {
272
+ id object = [[self registry] objectForId:(NSInteger)objectId];
273
+ if (![object isKindOfClass:[TiRtcReactNativeStoreExportBridge class]]) {
274
+ resolve(@{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"fileId" : (id)kCFNull,
275
+ @"filePath" : (id)kCFNull, @"durationMs" : (id)kCFNull});
276
+ return;
277
+ }
278
+ [(TiRtcReactNativeStoreExportBridge*)object await:resolve];
279
+ }
280
+
281
+ - (NSNumber*)storeExportStop:(double)objectId {
282
+ id object = [[self registry] objectForId:(NSInteger)objectId];
283
+ if (![object isKindOfClass:[TiRtcReactNativeStoreExportBridge class]]) {
284
+ return @(TiRtcReactNativeErrorObjectReleased);
285
+ }
286
+ TiStoreExportTask* task = ((TiRtcReactNativeStoreExportBridge*)object).task;
287
+ return @(TiRtcReactNativeNormalizeDarwin([task stop]));
288
+ }
289
+
290
+ - (NSNumber*)storeDispose:(double)objectId {
291
+ id object = [[self registry] objectForId:(NSInteger)objectId];
292
+ if (![object isKindOfClass:[TiStore class]]) return @(TiRtcReactNativeErrorObjectReleased);
293
+ int32_t code = [(TiStore*)object dispose];
294
+ if (code == 0) [[self registry] releaseObject:(NSInteger)objectId];
295
+ return @(TiRtcReactNativeNormalizeDarwin(code));
296
+ }
297
+
298
+ - (NSDictionary*)storeReplayGetInfo:(double)objectId {
299
+ id object = [[self registry] objectForId:(NSInteger)objectId];
300
+ if (![object isKindOfClass:[TiStoreReplay class]]) {
301
+ return @{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"speed" : @0,
302
+ @"currentTimeMs" : (id)kCFNull};
303
+ }
304
+ TiStoreReplay* replay = (TiStoreReplay*)object;
305
+ return @{@"code" : @0, @"speed" : @(TiRtcReactNativeStoreReplaySpeedValue(replay.speed)),
306
+ @"currentTimeMs" : replay.currentTimeMs ?: (id)kCFNull};
307
+ }
308
+
309
+ - (NSNumber*)storeReplayPlay:(double)objectId
310
+ startTimeMs:(double)startTimeMs
311
+ endTimeMs:(double)endTimeMs
312
+ initialTimeMs:(NSNumber*)initialTimeMs {
313
+ id object = [[self registry] objectForId:(NSInteger)objectId];
314
+ return [object isKindOfClass:[TiStoreReplay class]]
315
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreReplay*)object
316
+ playWithStartTimeMs:(int64_t)startTimeMs
317
+ endTimeMs:(int64_t)endTimeMs
318
+ initialTimeMs:initialTimeMs]))
319
+ : @(TiRtcReactNativeErrorObjectReleased);
320
+ }
321
+
322
+ - (NSNumber*)storeReplayPause:(double)objectId { return [self storeReplayCode:objectId selector:@selector(pause)]; }
323
+ - (NSNumber*)storeReplayResume:(double)objectId { return [self storeReplayCode:objectId selector:@selector(resume)]; }
324
+ - (NSNumber*)storeReplayStop:(double)objectId { return [self storeReplayCode:objectId selector:@selector(stop)]; }
325
+
326
+ - (NSNumber*)storeReplaySeek:(double)objectId timeMs:(double)timeMs {
327
+ id object = [[self registry] objectForId:(NSInteger)objectId];
328
+ return [object isKindOfClass:[TiStoreReplay class]]
329
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreReplay*)object seekToTimeMs:(int64_t)timeMs]))
330
+ : @(TiRtcReactNativeErrorObjectReleased);
331
+ }
332
+
333
+ - (NSNumber*)storeReplaySetSpeed:(double)objectId speed:(double)speed {
334
+ id object = [[self registry] objectForId:(NSInteger)objectId];
335
+ if (![object isKindOfClass:[TiStoreReplay class]]) return @(TiRtcReactNativeErrorObjectReleased);
336
+ TiStoreReplaySpeed mapped;
337
+ if (!TiRtcReactNativeStoreReplaySpeedFromValue((NSInteger)speed, &mapped)) return @(6000);
338
+ return @(TiRtcReactNativeNormalizeDarwin([(TiStoreReplay*)object setSpeed:mapped]));
339
+ }
340
+
341
+ - (NSDictionary*)storeReplayStartRecording:(double)objectId
342
+ videoChannelId:(double)videoChannelId
343
+ audioChannelId:(NSNumber*)audioChannelId {
344
+ id object = [[self registry] objectForId:(NSInteger)objectId];
345
+ if (![object isKindOfClass:[TiStoreReplay class]]) {
346
+ return @{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"taskId" : (id)kCFNull};
347
+ }
348
+ TiStoreRecordingTaskStartResult* started = [(TiStoreReplay*)object
349
+ startRecordingWithVideoChannelId:(NSInteger)videoChannelId audioChannelId:audioChannelId];
350
+ if (started.code != 0 || started.task == nil) {
351
+ return @{@"code" : @(TiRtcReactNativeNormalizeDarwin(started.code)), @"taskId" : (id)kCFNull};
352
+ }
353
+ NSInteger taskId = [[self registry] addObject:started.task];
354
+ return @{@"code" : @0, @"taskId" : @(taskId)};
355
+ }
356
+
357
+ - (void)storeRecordingTaskStop:(double)objectId
358
+ resolve:(RCTPromiseResolveBlock)resolve
359
+ reject:(RCTPromiseRejectBlock)reject {
360
+ id object = [[self registry] objectForId:(NSInteger)objectId];
361
+ if (![object isKindOfClass:[TiStoreRecordingTask class]]) {
362
+ resolve(@{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"fileId" : (id)kCFNull,
363
+ @"filePath" : (id)kCFNull, @"durationMs" : (id)kCFNull});
364
+ return;
365
+ }
366
+ [(TiStoreRecordingTask*)object stopWithCompletion:^(TiStoreRecordingResult* result) {
367
+ [[self registry] releaseObject:(NSInteger)objectId];
368
+ resolve(TiRtcReactNativeStoreMp4Result([self registry], result));
369
+ }];
370
+ }
371
+
372
+ - (NSNumber*)storeReplayDispose:(double)objectId {
373
+ id object = [[self registry] objectForId:(NSInteger)objectId];
374
+ if (![object isKindOfClass:[TiStoreReplay class]]) return @(TiRtcReactNativeErrorObjectReleased);
375
+ int32_t code = [(TiStoreReplay*)object dispose];
376
+ if (code == 0) [[self registry] releaseObject:(NSInteger)objectId];
377
+ return @(TiRtcReactNativeNormalizeDarwin(code));
378
+ }
379
+
380
+ - (NSNumber*)storeReplayCode:(double)objectId selector:(SEL)selector {
381
+ id object = [[self registry] objectForId:(NSInteger)objectId];
382
+ if (![object isKindOfClass:[TiStoreReplay class]]) return @(TiRtcReactNativeErrorObjectReleased);
383
+ IMP implementation = [object methodForSelector:selector];
384
+ int32_t (*call)(id, SEL) = (int32_t (*)(id, SEL))implementation;
385
+ return @(TiRtcReactNativeNormalizeDarwin(call(object, selector)));
386
+ }
387
+
388
+ - (NSDictionary*)storeCreateAudioOutput {
389
+ return [self
390
+ registerObject:^id { return [TiStoreAudioOutput new]; }
391
+ wire:^id(NSInteger objectId, id object) {
392
+ return [[self eventBinder] wireStoreAudioOutput:(TiStoreAudioOutput*)object objectId:objectId];
393
+ }];
394
+ }
395
+
396
+ - (NSDictionary*)storeAudioOutputGetState:(double)objectId {
397
+ id object = [[self registry] objectForId:(NSInteger)objectId];
398
+ if (![object isKindOfClass:[TiStoreAudioOutput class]]) {
399
+ return TiRtcReactNativeStateResult(TiRtcReactNativeErrorObjectReleased, nil);
400
+ }
401
+ return TiRtcReactNativeStateResult(0, TiRtcReactNativeStoreAudioState(((TiStoreAudioOutput*)object).state));
402
+ }
403
+
404
+ - (NSNumber*)storeAudioOutputSetVolume:(double)objectId volumePercent:(double)volumePercent {
405
+ id object = [[self registry] objectForId:(NSInteger)objectId];
406
+ return [object isKindOfClass:[TiStoreAudioOutput class]]
407
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreAudioOutput*)object setVolume:(uint32_t)volumePercent]))
408
+ : @(TiRtcReactNativeErrorObjectReleased);
409
+ }
410
+
411
+ - (NSNumber*)storeAudioOutputAttach:(double)outputId replayId:(double)replayId channelId:(double)channelId {
412
+ id output = [[self registry] objectForId:(NSInteger)outputId];
413
+ id replay = [[self registry] objectForId:(NSInteger)replayId];
414
+ return [output isKindOfClass:[TiStoreAudioOutput class]] && [replay isKindOfClass:[TiStoreReplay class]]
415
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreAudioOutput*)output
416
+ attachWithReplay:(TiStoreReplay*)replay channelId:(uint8_t)channelId]))
417
+ : @(TiRtcReactNativeErrorObjectReleased);
418
+ }
419
+
420
+ - (NSNumber*)storeAudioOutputDetach:(double)objectId {
421
+ id object = [[self registry] objectForId:(NSInteger)objectId];
422
+ return [object isKindOfClass:[TiStoreAudioOutput class]]
423
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreAudioOutput*)object detach]))
424
+ : @(TiRtcReactNativeErrorObjectReleased);
425
+ }
426
+
427
+ - (NSNumber*)storeAudioOutputDispose:(double)objectId {
428
+ id object = [[self registry] objectForId:(NSInteger)objectId];
429
+ if (![object isKindOfClass:[TiStoreAudioOutput class]]) return @(TiRtcReactNativeErrorObjectReleased);
430
+ int32_t code = [(TiStoreAudioOutput*)object dispose];
431
+ if (code == 0) [[self registry] releaseObject:(NSInteger)objectId];
432
+ return @(TiRtcReactNativeNormalizeDarwin(code));
433
+ }
434
+
435
+ - (NSDictionary*)storeCreateVideoOutput {
436
+ return [self
437
+ registerObject:^id { return [TiStoreVideoOutput new]; }
438
+ wire:^id(NSInteger objectId, id object) {
439
+ return [[self eventBinder] wireStoreVideoOutput:(TiStoreVideoOutput*)object objectId:objectId];
440
+ }];
441
+ }
442
+
443
+ - (NSDictionary*)storeVideoOutputGetState:(double)objectId {
444
+ id object = [[self registry] objectForId:(NSInteger)objectId];
445
+ if (![object isKindOfClass:[TiStoreVideoOutput class]]) {
446
+ return TiRtcReactNativeStateResult(TiRtcReactNativeErrorObjectReleased, nil);
447
+ }
448
+ return TiRtcReactNativeStateResult(0, TiRtcReactNativeStoreVideoState(((TiStoreVideoOutput*)object).state));
449
+ }
450
+
451
+ - (NSNumber*)storeVideoOutputAttach:(double)outputId replayId:(double)replayId channelId:(double)channelId {
452
+ id output = [[self registry] objectForId:(NSInteger)outputId];
453
+ id replay = [[self registry] objectForId:(NSInteger)replayId];
454
+ return [output isKindOfClass:[TiStoreVideoOutput class]] && [replay isKindOfClass:[TiStoreReplay class]]
455
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreVideoOutput*)output
456
+ attachWithReplay:(TiStoreReplay*)replay channelId:(uint8_t)channelId]))
457
+ : @(TiRtcReactNativeErrorObjectReleased);
458
+ }
459
+
460
+ - (NSNumber*)storeVideoOutputDetach:(double)objectId {
461
+ id object = [[self registry] objectForId:(NSInteger)objectId];
462
+ return [object isKindOfClass:[TiStoreVideoOutput class]]
463
+ ? @(TiRtcReactNativeNormalizeDarwin([(TiStoreVideoOutput*)object detach]))
464
+ : @(TiRtcReactNativeErrorObjectReleased);
465
+ }
466
+
467
+ - (void)storeVideoOutputTakeSnapshot:(double)objectId
468
+ resolve:(RCTPromiseResolveBlock)resolve
469
+ reject:(RCTPromiseRejectBlock)reject {
470
+ id object = [[self registry] objectForId:(NSInteger)objectId];
471
+ if (![object isKindOfClass:[TiStoreVideoOutput class]]) {
472
+ resolve(@{@"code" : @(TiRtcReactNativeErrorObjectReleased), @"fileId" : (id)kCFNull,
473
+ @"filePath" : (id)kCFNull});
474
+ return;
475
+ }
476
+ TiStoreVideoOutput* output = (TiStoreVideoOutput*)object;
477
+ dispatch_async(dispatch_get_main_queue(), ^{
478
+ [output takeSnapshotWithCompletion:^(TiStoreSnapshotResult* result) {
479
+ NSInteger fileId = result.file == nil ? 0 : [[self registry] addTransientObject:result.file];
480
+ resolve(@{@"code" : @(TiRtcReactNativeNormalizeDarwin(result.code)),
481
+ @"fileId" : fileId == 0 ? (id)kCFNull : @(fileId),
482
+ @"filePath" : result.file.path ?: (id)kCFNull});
483
+ }];
484
+ });
485
+ }
486
+
487
+ - (NSNumber*)storeVideoOutputDispose:(double)objectId {
488
+ id object = [[self registry] objectForId:(NSInteger)objectId];
489
+ if (![object isKindOfClass:[TiStoreVideoOutput class]]) return @(TiRtcReactNativeErrorObjectReleased);
490
+ int32_t code = [(TiStoreVideoOutput*)object dispose];
491
+ if (code == 0) [[self registry] releaseObject:(NSInteger)objectId];
492
+ return @(TiRtcReactNativeNormalizeDarwin(code));
493
+ }
494
+
495
+ @end
496
+ #pragma clang diagnostic pop
@@ -1,6 +1,7 @@
1
1
  #import <Foundation/Foundation.h>
2
+ #import <React/RCTInvalidating.h>
2
3
 
3
4
  #import "TiRtcSpec.h"
4
5
 
5
- @interface TiRtcReactNative : NativeTiRtcSpecBase <NativeTiRtcSpec>
6
+ @interface TiRtcReactNative : NativeTiRtcSpecBase <NativeTiRtcSpec, RCTInvalidating>
6
7
  @end
@@ -1,7 +1,9 @@
1
1
  #import "TiRtcReactNative.h"
2
+ #import "TiRtcReactNative+Private.h"
2
3
 
3
4
  #import <React/RCTBridgeModule.h>
4
5
 
6
+ #import <atomic>
5
7
  #import <memory>
6
8
 
7
9
  #import "TiRtcReactNativeErrors.h"
@@ -12,23 +14,51 @@
12
14
  #import "TiRtcReactNativeOptions.h"
13
15
  #import "TiRtcReactNativeRegistry.h"
14
16
 
17
+ @protocol TiRtcReactNativeMediaFile <NSObject>
18
+ - (void)deleteWithCompletion:(void (^)(int32_t code))completion;
19
+ @end
20
+
21
+ static void TiRtcReactNativeLogInvalidationFailure(NSString* operation, int32_t code) {
22
+ if (code == 0 || code == TiRtcReactNativeErrorObjectReleased) return;
23
+ [TiRtcLogging logWithLevel:TiRtcLogLevelWarning
24
+ tag:@"TIRTC_REACT_NATIVE"
25
+ message:[NSString stringWithFormat:
26
+ @"[module_invalidate] operation=%@ error_code=%d",
27
+ operation, code]];
28
+ }
29
+
30
+ static void TiRtcReactNativeDeleteForInvalidation(id file, dispatch_group_t group) {
31
+ if (file == nil || ![file respondsToSelector:@selector(deleteWithCompletion:)]) return;
32
+ dispatch_group_enter(group);
33
+ [(id<TiRtcReactNativeMediaFile>)file deleteWithCompletion:^(int32_t code) {
34
+ TiRtcReactNativeLogInvalidationFailure(@"media_delete", code);
35
+ dispatch_group_leave(group);
36
+ }];
37
+ }
38
+
15
39
  #pragma clang diagnostic push
16
40
  #pragma clang diagnostic ignored "-Wprotocol"
17
41
  @implementation TiRtcReactNative {
18
42
  TiRtcReactNativeRegistry* _registry;
19
43
  TiRtcReactNativeEventSink* _eventSink;
20
44
  TiRtcReactNativeEventBinder* _eventBinder;
45
+ std::atomic_bool _invalidated;
21
46
  }
22
47
 
23
48
  RCT_EXPORT_MODULE(TiRtc)
24
49
 
25
50
  - (instancetype)init {
26
51
  if (self = [super init]) {
52
+ _invalidated.store(false);
27
53
  _registry = [TiRtcReactNativeRegistry sharedRegistry];
28
54
  __weak TiRtcReactNative* weakSelf = self;
29
55
  _eventSink = [[TiRtcReactNativeEventSink alloc] initWithRegistry:_registry
30
56
  emitBlock:^(NSDictionary* event) {
31
- [weakSelf emitOnTiRtcEvent:event];
57
+ TiRtcReactNative* strongSelf = weakSelf;
58
+ if (strongSelf != nil &&
59
+ !strongSelf->_invalidated.load()) {
60
+ [strongSelf emitOnTiRtcEvent:event];
61
+ }
32
62
  }];
33
63
  [_registry setEventSink:_eventSink];
34
64
  _eventBinder = [[TiRtcReactNativeEventBinder alloc] initWithEventSink:_eventSink];
@@ -36,6 +66,84 @@ RCT_EXPORT_MODULE(TiRtc)
36
66
  return self;
37
67
  }
38
68
 
69
+ - (void)invalidate {
70
+ if (_invalidated.exchange(true)) return;
71
+ NSArray* objects = [_registry drainObjects];
72
+ NSArray* files = [_registry drainTransientObjects];
73
+ dispatch_group_t pending = dispatch_group_create();
74
+ for (id file in files) TiRtcReactNativeDeleteForInvalidation(file, pending);
75
+ for (id object in objects) {
76
+ if ([object isKindOfClass:[TiRtcRecordingTask class]]) {
77
+ dispatch_group_enter(pending);
78
+ [(TiRtcRecordingTask*)object stopWithCompletion:^(TiRtcRecordingResult* result) {
79
+ TiRtcReactNativeDeleteForInvalidation(result.file, pending);
80
+ dispatch_group_leave(pending);
81
+ }];
82
+ } else if ([object isKindOfClass:[TiStoreRecordingTask class]]) {
83
+ dispatch_group_enter(pending);
84
+ [(TiStoreRecordingTask*)object stopWithCompletion:^(TiStoreRecordingResult* result) {
85
+ TiRtcReactNativeDeleteForInvalidation(result.file, pending);
86
+ dispatch_group_leave(pending);
87
+ }];
88
+ } else if ([object conformsToProtocol:@protocol(TiRtcReactNativeReloadTask)]) {
89
+ dispatch_group_enter(pending);
90
+ [(id<TiRtcReactNativeReloadTask>)object
91
+ stopForReactNativeInvalidation:^(id file) {
92
+ TiRtcReactNativeDeleteForInvalidation(file, pending);
93
+ dispatch_group_leave(pending);
94
+ }];
95
+ }
96
+ }
97
+ dispatch_group_notify(pending, dispatch_get_main_queue(), ^{
98
+ for (TiStoreAudioOutput* output in objects) {
99
+ if (![output isKindOfClass:[TiStoreAudioOutput class]]) continue;
100
+ TiRtcReactNativeLogInvalidationFailure(@"store_audio_detach", [output detach]);
101
+ TiRtcReactNativeLogInvalidationFailure(@"store_audio_dispose", [output dispose]);
102
+ }
103
+ for (TiStoreVideoOutput* output in objects) {
104
+ if (![output isKindOfClass:[TiStoreVideoOutput class]]) continue;
105
+ TiRtcReactNativeLogInvalidationFailure(@"store_video_detach", [output detach]);
106
+ TiRtcReactNativeLogInvalidationFailure(@"store_video_view_detach", [output detachView]);
107
+ TiRtcReactNativeLogInvalidationFailure(@"store_video_dispose", [output dispose]);
108
+ }
109
+ for (TiRtcAudioOutput* output in objects) {
110
+ if ([output isKindOfClass:[TiRtcAudioOutput class]])
111
+ TiRtcReactNativeLogInvalidationFailure(@"audio_output_dispose", [output dispose]);
112
+ }
113
+ for (TiRtcVideoOutput* output in objects) {
114
+ if ([output isKindOfClass:[TiRtcVideoOutput class]])
115
+ TiRtcReactNativeLogInvalidationFailure(@"video_output_dispose", [output dispose]);
116
+ }
117
+ for (TiRtcAudioInput* input in objects) {
118
+ if ([input isKindOfClass:[TiRtcAudioInput class]])
119
+ TiRtcReactNativeLogInvalidationFailure(@"audio_input_dispose", [input dispose]);
120
+ }
121
+ for (TiRtcVideoInput* input in objects) {
122
+ if ([input isKindOfClass:[TiRtcVideoInput class]])
123
+ TiRtcReactNativeLogInvalidationFailure(@"video_input_dispose", [input dispose]);
124
+ }
125
+ for (TiStoreReplay* replay in objects) {
126
+ if (![replay isKindOfClass:[TiStoreReplay class]]) continue;
127
+ TiRtcReactNativeLogInvalidationFailure(@"store_replay_stop", [replay stop]);
128
+ TiRtcReactNativeLogInvalidationFailure(@"store_replay_dispose", [replay dispose]);
129
+ }
130
+ for (TiRtcConn* conn in objects) {
131
+ if (![conn isKindOfClass:[TiRtcConn class]]) continue;
132
+ TiRtcReactNativeLogInvalidationFailure(@"conn_disconnect", [conn disconnect]);
133
+ TiRtcReactNativeLogInvalidationFailure(@"conn_dispose", [conn dispose]);
134
+ }
135
+ for (TiStore* store in objects) {
136
+ if ([store isKindOfClass:[TiStore class]])
137
+ TiRtcReactNativeLogInvalidationFailure(@"store_dispose", [store dispose]);
138
+ }
139
+ TiRtcReactNativeLogInvalidationFailure(@"store_shutdown", [TiStore shutdown]);
140
+ TiRtcReactNativeLogInvalidationFailure(@"runtime_shutdown", [TiRtc shutdown]);
141
+ [TiRtcLogging logWithLevel:TiRtcLogLevelInfo
142
+ tag:@"TIRTC_REACT_NATIVE"
143
+ message:@"[module_invalidate] native owners released"];
144
+ });
145
+ }
146
+
39
147
  - (void)initialize:(JS::NativeTiRtc::NativeInitOptions&)options
40
148
  resolve:(RCTPromiseResolveBlock)resolve
41
149
  reject:(RCTPromiseRejectBlock)reject {
@@ -6,6 +6,10 @@
6
6
  @class TiRtcReactNativeEventSink;
7
7
  @class TiRtcVideoInput;
8
8
  @class TiRtcVideoOutput;
9
+ @class TiStore;
10
+ @class TiStoreAudioOutput;
11
+ @class TiStoreReplay;
12
+ @class TiStoreVideoOutput;
9
13
 
10
14
  @interface TiRtcReactNativeEventBinder : NSObject
11
15
 
@@ -15,5 +19,9 @@
15
19
  - (id)wireVideoOutput:(TiRtcVideoOutput*)output objectId:(NSInteger)objectId;
16
20
  - (id)wireAudioInput:(TiRtcAudioInput*)input objectId:(NSInteger)objectId;
17
21
  - (id)wireVideoInput:(TiRtcVideoInput*)input objectId:(NSInteger)objectId;
22
+ - (id)wireStore:(TiStore*)store objectId:(NSInteger)objectId;
23
+ - (id)wireStoreReplay:(TiStoreReplay*)replay objectId:(NSInteger)objectId;
24
+ - (id)wireStoreAudioOutput:(TiStoreAudioOutput*)output objectId:(NSInteger)objectId;
25
+ - (id)wireStoreVideoOutput:(TiStoreVideoOutput*)output objectId:(NSInteger)objectId;
18
26
 
19
27
  @end